@inflexa-ai/harness 0.31.1 → 0.32.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/conversation-agent.d.ts +7 -0
- package/dist/agents/conversation-agent.js +1 -0
- package/dist/agents/report-session-agent.d.ts +9 -0
- package/dist/agents/report-session-agent.js +10 -2
- package/dist/app/chat-turn.d.ts +11 -0
- package/dist/app/chat-turn.js +17 -1
- package/dist/app/spawn-report-session.d.ts +12 -0
- package/dist/app/spawn-report-session.js +21 -1
- package/dist/contracts/report-blocks.d.ts +8 -8
- package/dist/contracts/schemas/chat-parts.d.ts +6 -6
- package/dist/index.d.ts +4 -1
- package/dist/index.js +8 -0
- package/dist/provenance/seam.d.ts +203 -0
- package/dist/provenance/seam.js +71 -0
- package/dist/report-model/authoring-grammar.d.ts +2 -2
- package/dist/report-render/assets.d.ts +10 -0
- package/dist/report-render/assets.js +14 -0
- package/dist/report-render/design.js +256 -0
- package/dist/report-render/page.d.ts +75 -0
- package/dist/report-render/page.js +793 -1
- package/dist/report-render/provenance-data.d.ts +42 -0
- package/dist/report-render/provenance-data.js +62 -0
- package/dist/report-render/render.d.ts +20 -1
- package/dist/report-render/render.js +23 -10
- package/dist/report-render/types.d.ts +3 -0
- package/dist/report-render/views/chart-view.d.ts +5 -1
- package/dist/report-render/views/chart-view.js +6 -2
- package/dist/report-render/views/lineage.d.ts +93 -0
- package/dist/report-render/views/lineage.js +86 -0
- package/dist/report-render/views/page-view.d.ts +22 -1
- package/dist/report-render/views/page-view.js +12 -3
- package/dist/report-render/views/prose.d.ts +7 -1
- package/dist/report-render/views/prose.js +10 -6
- package/dist/report-render/views/references-view.d.ts +17 -1
- package/dist/report-render/views/references-view.js +12 -4
- package/dist/report-render/views/values.d.ts +17 -4
- package/dist/report-render/views/values.js +21 -8
- package/dist/runtime/assemble.d.ts +22 -0
- package/dist/runtime/assemble.js +4 -0
- package/dist/sandbox/k8s-client.d.ts +0 -5
- package/dist/sandbox/k8s-client.js +9 -115
- package/dist/schemas/artifact-manifest.d.ts +4 -4
- package/dist/tools/display/show-user.d.ts +1 -1
- package/dist/tools/report-authoring/authoring-tools.d.ts +16 -1
- package/dist/tools/report-authoring/authoring-tools.js +58 -16
- package/dist/tools/report-session/derive-table.d.ts +3 -0
- package/dist/tools/report-session/derive-table.js +15 -0
- package/dist/tools/report-session/preview-report.d.ts +13 -4
- package/dist/tools/report-session/preview-report.js +23 -6
- package/dist/tools/report-session/record-version.d.ts +3 -0
- package/dist/tools/report-session/record-version.js +7 -3
- package/dist/tools/start-report-session.d.ts +7 -0
- package/dist/tools/start-report-session.js +1 -0
- package/dist/workflows/execute-analysis.d.ts +15 -69
- package/dist/workflows/execute-analysis.js +5 -4
- package/package.json +2 -1
|
@@ -35,6 +35,7 @@ import { type BioToolKeys } from "../tools/bio/keys.js";
|
|
|
35
35
|
import type { EnvironmentStorePaths } from "../config/environment-stores.js";
|
|
36
36
|
import type { RunAuthorizer } from "../execution/run-authorizer.js";
|
|
37
37
|
import type { RunLauncher } from "../execution/run-launcher.js";
|
|
38
|
+
import type { ProvenanceSeam } from "../provenance/seam.js";
|
|
38
39
|
import type { Logger } from "../lib/logger.js";
|
|
39
40
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
40
41
|
import type { CitationResolver } from "../citations/types.js";
|
|
@@ -111,6 +112,12 @@ export interface ConversationAgentDeps extends EnvironmentStorePaths {
|
|
|
111
112
|
* at a page reads that same answer.
|
|
112
113
|
*/
|
|
113
114
|
readonly eyes?: AcquireEyes;
|
|
115
|
+
/**
|
|
116
|
+
* The provenance seam. `start_report_session` hands it to its spawn, which
|
|
117
|
+
* emits the creation of the session at the moment that the child lands. No
|
|
118
|
+
* other tool of this roster reads it.
|
|
119
|
+
*/
|
|
120
|
+
readonly provenance?: ProvenanceSeam;
|
|
114
121
|
/**
|
|
115
122
|
* Host resource policy — per-step ceilings + machine budget. `generate_plan`
|
|
116
123
|
* states the ceilings to the planner and validates against them;
|
|
@@ -122,6 +122,7 @@ export function createConversationAgent(deps) {
|
|
|
122
122
|
chrome,
|
|
123
123
|
anchorSession: deps.anchorReportSession,
|
|
124
124
|
...(deps.eyes ? { eyes: deps.eyes } : {}),
|
|
125
|
+
...(deps.provenance ? { provenance: deps.provenance } : {}),
|
|
125
126
|
...(deps.logger ? { logger: deps.logger } : {}),
|
|
126
127
|
}),
|
|
127
128
|
// Display.
|
|
@@ -38,6 +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 { ProvenanceSeam } from "../provenance/seam.js";
|
|
41
42
|
import { type MakeSessionPagePublisher, type ResolvePageAsset, type ResolvePageUrl } from "../tools/report-session/index.js";
|
|
42
43
|
/** Canonical agent id -- distinct from the id of the old templating agent. */
|
|
43
44
|
export declare const REPORT_SESSION_AGENT_ID: "report-session";
|
|
@@ -114,6 +115,14 @@ export interface ReportSessionAgentDeps {
|
|
|
114
115
|
* tool navigates through a `file://` URL of the page path.
|
|
115
116
|
*/
|
|
116
117
|
readonly resolvePageUrl?: ResolvePageUrl;
|
|
118
|
+
/**
|
|
119
|
+
* The provenance seam -- the acts of the session as typed events, and the frozen document of the
|
|
120
|
+
* analysis as opaque bytes. The tools that change the draft, derive a table, preview the page, and
|
|
121
|
+
* record a version each emit through the session member, thus one bound member carries the whole
|
|
122
|
+
* session. The preview asks the read member before each render, and the page then carries the
|
|
123
|
+
* document. An unbound member emits nothing, or gives absence, and every tool acts the same.
|
|
124
|
+
*/
|
|
125
|
+
readonly provenance?: ProvenanceSeam;
|
|
117
126
|
/** Operational logging seam; omitted falls back to no-op. */
|
|
118
127
|
readonly logger?: Logger;
|
|
119
128
|
}
|
|
@@ -39,8 +39,11 @@ const REPORT_SESSION_MAX_ITERATIONS = 200;
|
|
|
39
39
|
/** Build the report `AgentDefinition` with every tool bound to its deps. */
|
|
40
40
|
export function createReportSessionAgent(deps) {
|
|
41
41
|
const { model, pool, embedding, workspaceFs, gateway, resolveWorkspaceRoot, store, threads, chrome, eyes, makeResolver, resolvePageAsset, logger } = deps;
|
|
42
|
-
const { derivations, runDerivation, runAuthorizer, makeSessionPages, resolvePageUrl } = deps;
|
|
43
|
-
|
|
42
|
+
const { derivations, runDerivation, runAuthorizer, makeSessionPages, resolvePageUrl, provenance } = deps;
|
|
43
|
+
// The seam rides into each tool that lands an act of the session, thus the composition binds it one
|
|
44
|
+
// time and no tool reads a second source.
|
|
45
|
+
const observation = provenance ? { provenance } : {};
|
|
46
|
+
const authoring = createReportAuthoringTools(gateway, { ...observation, ...(logger ? { logger } : {}) });
|
|
44
47
|
const tools = [
|
|
45
48
|
// The read surface toward the analysis. It roams the tree read-only.
|
|
46
49
|
createReadFileTool(workspaceFs),
|
|
@@ -75,6 +78,7 @@ export function createReportSessionAgent(deps) {
|
|
|
75
78
|
derivations,
|
|
76
79
|
...(runDerivation ? { runDerivation } : {}),
|
|
77
80
|
...(runAuthorizer ? { runAuthorizer } : {}),
|
|
81
|
+
...observation,
|
|
78
82
|
...(logger ? { logger } : {}),
|
|
79
83
|
}),
|
|
80
84
|
// The render-and-preview tool. It resolves the page root per call.
|
|
@@ -84,6 +88,9 @@ export function createReportSessionAgent(deps) {
|
|
|
84
88
|
...(makeResolver ? { makeResolver } : {}),
|
|
85
89
|
...(resolvePageAsset ? { resolvePageAsset } : {}),
|
|
86
90
|
...(makeSessionPages ? { makeSessionPages } : {}),
|
|
91
|
+
// The preview is the one site that carries the document into the page, thus the read member
|
|
92
|
+
// reaches this tool alone.
|
|
93
|
+
...observation,
|
|
87
94
|
...(logger ? { logger } : {}),
|
|
88
95
|
}),
|
|
89
96
|
// The eyes tool. It opens the rendered page in headless Chrome.
|
|
@@ -103,6 +110,7 @@ export function createReportSessionAgent(deps) {
|
|
|
103
110
|
threads,
|
|
104
111
|
resolveWorkspaceRoot,
|
|
105
112
|
...(makeResolver ? { makeResolver } : {}),
|
|
113
|
+
...observation,
|
|
106
114
|
...(logger ? { logger } : {}),
|
|
107
115
|
}),
|
|
108
116
|
];
|
package/dist/app/chat-turn.d.ts
CHANGED
|
@@ -16,10 +16,21 @@ import type { Pool } from "pg";
|
|
|
16
16
|
import { type ThreadType } from "../memory/thread-store.js";
|
|
17
17
|
import { type AssembledMessages } from "./message-assembly.js";
|
|
18
18
|
import type { Logger } from "../lib/logger.js";
|
|
19
|
+
import { type ProvenanceSeam } from "../provenance/seam.js";
|
|
19
20
|
export interface PrepareChatTurnDeps {
|
|
20
21
|
/** Operational logging seam; omitted falls back to no-op. */
|
|
21
22
|
readonly logger?: Logger;
|
|
22
23
|
readonly pool: Pool;
|
|
24
|
+
/**
|
|
25
|
+
* The provenance seam. The turn reads its session emit member, and no other
|
|
26
|
+
* member. The turn holds the one site that writes the conversation thread of
|
|
27
|
+
* an analysis, thus it is the one site that can tell the embedder the true
|
|
28
|
+
* moment of that creation.
|
|
29
|
+
*
|
|
30
|
+
* The member is optional and fire-and-forget, the same as it is at each site
|
|
31
|
+
* of a report session.
|
|
32
|
+
*/
|
|
33
|
+
readonly provenance?: ProvenanceSeam;
|
|
23
34
|
}
|
|
24
35
|
export interface PrepareChatTurnParams {
|
|
25
36
|
readonly analysisId: string;
|
package/dist/app/chat-turn.js
CHANGED
|
@@ -21,6 +21,7 @@ import { loadAnalysisStatus, queryNonTerminalRunsByAnalysis } from "../state/ind
|
|
|
21
21
|
import { assembleMessages } from "./message-assembly.js";
|
|
22
22
|
import { renderRunActivity, renderRunActivityUnavailable, RUN_ACTIVITY_DETAIL_LIMIT } from "./run-activity.js";
|
|
23
23
|
import { createNoopLogger } from "../lib/console-logger.js";
|
|
24
|
+
import { bindSessionEmit } from "../provenance/seam.js";
|
|
24
25
|
/**
|
|
25
26
|
* Prepare one chat turn: resolve thread ownership, seed the title, load
|
|
26
27
|
* analysis status, and assemble the message array. Input sanitization stays
|
|
@@ -29,6 +30,10 @@ import { createNoopLogger } from "../lib/console-logger.js";
|
|
|
29
30
|
export async function prepareChatTurn(deps, params) {
|
|
30
31
|
const { pool } = deps;
|
|
31
32
|
const { analysisId, threadId, userInput } = params;
|
|
33
|
+
const logger = (deps.logger ?? createNoopLogger()).named("harness.chat");
|
|
34
|
+
// An unbound member gives a call that does nothing, thus the emit below needs
|
|
35
|
+
// no test of its own.
|
|
36
|
+
const observe = bindSessionEmit(deps.provenance, logger);
|
|
32
37
|
// Ownership check before any read/write of the thread — a `threadId`
|
|
33
38
|
// owned by a different analysis is indistinguishable from a missing one.
|
|
34
39
|
const store = createThreadStore(pool);
|
|
@@ -54,13 +59,24 @@ export async function prepareChatTurn(deps, params) {
|
|
|
54
59
|
title: deriveThreadTitle(userInput),
|
|
55
60
|
}));
|
|
56
61
|
threadType = created.threadType;
|
|
62
|
+
// The row that comes back is not proof that this call wrote it. The read
|
|
63
|
+
// above hides an archived thread, and the idempotent insert then reads
|
|
64
|
+
// that same row back. Thus a turn on an archived thread reaches here with
|
|
65
|
+
// a row that a different site created, and an emit would record a start
|
|
66
|
+
// that never happened.
|
|
67
|
+
//
|
|
68
|
+
// This site writes a live conversation thread and nothing else. An
|
|
69
|
+
// archived row, or a row of another kind, is therefore one that already
|
|
70
|
+
// existed. A root session has no parent, thus the event carries none.
|
|
71
|
+
if (created.deletedAt === null && created.threadType === "conversation") {
|
|
72
|
+
observe({ type: "create-session", analysisId, threadId, sessionKind: created.threadType });
|
|
73
|
+
}
|
|
57
74
|
}
|
|
58
75
|
else if (!existing.title || existing.title.length === 0) {
|
|
59
76
|
unwrapOrThrow(await store.updateTitle(threadId, deriveThreadTitle(userInput)));
|
|
60
77
|
}
|
|
61
78
|
}
|
|
62
79
|
catch (err) {
|
|
63
|
-
const logger = (deps.logger ?? createNoopLogger()).named("harness.chat");
|
|
64
80
|
logger.warn("title-seed failed (non-fatal)", logger.errorFields(err));
|
|
65
81
|
}
|
|
66
82
|
const analysisState = await loadAnalysisStatus(pool, analysisId).unwrapOr(null);
|
|
@@ -66,6 +66,7 @@ import type { AcquireEyes } from "../lib/eyes.js";
|
|
|
66
66
|
import type { Logger } from "../lib/logger.js";
|
|
67
67
|
import type { CapturePage } from "../lib/page-capture.js";
|
|
68
68
|
import { type Thread, type ThreadInputError, type ThreadPage, type ThreadType } from "../memory/thread-store.js";
|
|
69
|
+
import { type ProvenanceSeam } from "../provenance/seam.js";
|
|
69
70
|
import type { EnsureSessionStateResult } from "./report-session-runtime.js";
|
|
70
71
|
/**
|
|
71
72
|
* A spawn the operation refuses before it writes a row. Distinct from
|
|
@@ -192,6 +193,17 @@ export interface ReportSessionSpawnDeps {
|
|
|
192
193
|
* is idempotent.
|
|
193
194
|
*/
|
|
194
195
|
readonly anchorSession?: (threadId: string) => Promise<EnsureSessionStateResult>;
|
|
196
|
+
/**
|
|
197
|
+
* The provenance seam. The spawn reads its session emit member, and no other
|
|
198
|
+
* member. The spawn is the birth of the session, thus it is the one site that
|
|
199
|
+
* can tell the embedder the true moment of the ask. A consumer that waits for
|
|
200
|
+
* the first authoring act reads a later moment, and it reads nothing at all
|
|
201
|
+
* for a session that stays empty.
|
|
202
|
+
*
|
|
203
|
+
* The member is optional and fire-and-forget, the same as it is at each
|
|
204
|
+
* authoring site.
|
|
205
|
+
*/
|
|
206
|
+
readonly provenance?: ProvenanceSeam;
|
|
195
207
|
/** Operational logging seam. An omitted logger falls back to the no-op. */
|
|
196
208
|
readonly logger?: Logger;
|
|
197
209
|
}
|
|
@@ -65,6 +65,7 @@ import { createNoopLogger } from "../lib/console-logger.js";
|
|
|
65
65
|
import { conversationRecordTurn, createThreadHistory } from "../memory/thread-history.js";
|
|
66
66
|
import { createThreadStore } from "../memory/thread-store.js";
|
|
67
67
|
import { createWorkingMemory } from "../memory/working-memory.js";
|
|
68
|
+
import { bindSessionEmit } from "../provenance/seam.js";
|
|
68
69
|
const _assertDomainError = true;
|
|
69
70
|
/**
|
|
70
71
|
* Whether the composition gives a route to a look at the rendered page. The
|
|
@@ -165,6 +166,9 @@ export function createReportSessionSpawn(deps) {
|
|
|
165
166
|
// The routes are fixed at construction, thus the gate reads one boolean and
|
|
166
167
|
// never a live probe of the sidecar.
|
|
167
168
|
const eyesAvailable = compositionHasEyes(deps);
|
|
169
|
+
// An unbound member gives a call that does nothing, thus the spawn emits at
|
|
170
|
+
// one site with no test of its own.
|
|
171
|
+
const observe = bindSessionEmit(deps.provenance, log);
|
|
168
172
|
/**
|
|
169
173
|
* Write the one seed message of the child and give the child back. The
|
|
170
174
|
* message rides as a record, not as user input: nobody typed it in the child
|
|
@@ -250,7 +254,7 @@ export function createReportSessionSpawn(deps) {
|
|
|
250
254
|
if (!eyesAvailable) {
|
|
251
255
|
return errAsync({ type: "no_browser", op: OP, parentThreadId, detail: NO_BROWSER_DETAIL });
|
|
252
256
|
}
|
|
253
|
-
|
|
257
|
+
const spawned = store.getThread(parentThreadId).andThen((parent) => {
|
|
254
258
|
// An absent parent and an archived one arrive the same way — `getThread`
|
|
255
259
|
// filters the tombstone — and both refuse as `parent_not_found`.
|
|
256
260
|
if (parent === null) {
|
|
@@ -288,6 +292,22 @@ export function createReportSessionSpawn(deps) {
|
|
|
288
292
|
.andThen((child) => ResultAsync.fromSafePromise(pinSessionSnapshot(child))));
|
|
289
293
|
});
|
|
290
294
|
});
|
|
295
|
+
// The emit rides the ok channel alone, thus a refusal and a fault tell the
|
|
296
|
+
// embedder nothing. The child row is on disk at this point, and no
|
|
297
|
+
// authoring act of the session ran yet.
|
|
298
|
+
//
|
|
299
|
+
// The kind comes from the row and not from the input, because the insert
|
|
300
|
+
// is idempotent and the store tells a caller to read the type back.
|
|
301
|
+
return spawned.map((child) => {
|
|
302
|
+
observe({
|
|
303
|
+
type: "create-session",
|
|
304
|
+
analysisId: child.analysisId,
|
|
305
|
+
threadId: child.threadId,
|
|
306
|
+
sessionKind: child.threadType,
|
|
307
|
+
parentThreadId,
|
|
308
|
+
});
|
|
309
|
+
return child;
|
|
310
|
+
});
|
|
291
311
|
}
|
|
292
312
|
function listReportSessions(analysisId, paging = {}) {
|
|
293
313
|
return store.listThreads({ analysisId, type: "report", ...paging });
|
|
@@ -200,15 +200,15 @@ export declare const ChartAxesSchema: z.ZodObject<{
|
|
|
200
200
|
x: z.ZodOptional<z.ZodObject<{
|
|
201
201
|
title: z.ZodOptional<z.ZodString>;
|
|
202
202
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
203
|
-
log: "log";
|
|
204
203
|
linear: "linear";
|
|
204
|
+
log: "log";
|
|
205
205
|
}>>;
|
|
206
206
|
}, z.core.$strict>>;
|
|
207
207
|
y: z.ZodOptional<z.ZodObject<{
|
|
208
208
|
title: z.ZodOptional<z.ZodString>;
|
|
209
209
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
210
|
-
log: "log";
|
|
211
210
|
linear: "linear";
|
|
211
|
+
log: "log";
|
|
212
212
|
}>>;
|
|
213
213
|
}, z.core.$strict>>;
|
|
214
214
|
}, z.core.$strict>;
|
|
@@ -297,15 +297,15 @@ export declare const ChartCompositionSchema: z.ZodObject<{
|
|
|
297
297
|
x: z.ZodOptional<z.ZodObject<{
|
|
298
298
|
title: z.ZodOptional<z.ZodString>;
|
|
299
299
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
300
|
-
log: "log";
|
|
301
300
|
linear: "linear";
|
|
301
|
+
log: "log";
|
|
302
302
|
}>>;
|
|
303
303
|
}, z.core.$strict>>;
|
|
304
304
|
y: z.ZodOptional<z.ZodObject<{
|
|
305
305
|
title: z.ZodOptional<z.ZodString>;
|
|
306
306
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
307
|
-
log: "log";
|
|
308
307
|
linear: "linear";
|
|
308
|
+
log: "log";
|
|
309
309
|
}>>;
|
|
310
310
|
}, z.core.$strict>>;
|
|
311
311
|
}, z.core.$strict>>;
|
|
@@ -724,15 +724,15 @@ export declare const ChartBlockSchema: z.ZodObject<{
|
|
|
724
724
|
x: z.ZodOptional<z.ZodObject<{
|
|
725
725
|
title: z.ZodOptional<z.ZodString>;
|
|
726
726
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
727
|
-
log: "log";
|
|
728
727
|
linear: "linear";
|
|
728
|
+
log: "log";
|
|
729
729
|
}>>;
|
|
730
730
|
}, z.core.$strict>>;
|
|
731
731
|
y: z.ZodOptional<z.ZodObject<{
|
|
732
732
|
title: z.ZodOptional<z.ZodString>;
|
|
733
733
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
734
|
-
log: "log";
|
|
735
734
|
linear: "linear";
|
|
735
|
+
log: "log";
|
|
736
736
|
}>>;
|
|
737
737
|
}, z.core.$strict>>;
|
|
738
738
|
}, z.core.$strict>>;
|
|
@@ -1168,15 +1168,15 @@ export declare const ATOM_BLOCK_SCHEMAS: readonly [z.ZodObject<{
|
|
|
1168
1168
|
x: z.ZodOptional<z.ZodObject<{
|
|
1169
1169
|
title: z.ZodOptional<z.ZodString>;
|
|
1170
1170
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
1171
|
-
log: "log";
|
|
1172
1171
|
linear: "linear";
|
|
1172
|
+
log: "log";
|
|
1173
1173
|
}>>;
|
|
1174
1174
|
}, z.core.$strict>>;
|
|
1175
1175
|
y: z.ZodOptional<z.ZodObject<{
|
|
1176
1176
|
title: z.ZodOptional<z.ZodString>;
|
|
1177
1177
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
1178
|
-
log: "log";
|
|
1179
1178
|
linear: "linear";
|
|
1179
|
+
log: "log";
|
|
1180
1180
|
}>>;
|
|
1181
1181
|
}, z.core.$strict>>;
|
|
1182
1182
|
}, z.core.$strict>>;
|
|
@@ -240,9 +240,9 @@ export declare const StepOutputFileSchema: z.ZodObject<{
|
|
|
240
240
|
fileType: z.ZodEnum<{
|
|
241
241
|
output: "output";
|
|
242
242
|
summary: "summary";
|
|
243
|
-
script: "script";
|
|
244
|
-
figure: "figure";
|
|
245
243
|
log: "log";
|
|
244
|
+
figure: "figure";
|
|
245
|
+
script: "script";
|
|
246
246
|
notebook: "notebook";
|
|
247
247
|
}>;
|
|
248
248
|
description: z.ZodString;
|
|
@@ -259,9 +259,9 @@ export declare const StepOutputPartSchema: z.ZodObject<{
|
|
|
259
259
|
fileType: z.ZodEnum<{
|
|
260
260
|
output: "output";
|
|
261
261
|
summary: "summary";
|
|
262
|
-
script: "script";
|
|
263
|
-
figure: "figure";
|
|
264
262
|
log: "log";
|
|
263
|
+
figure: "figure";
|
|
264
|
+
script: "script";
|
|
265
265
|
notebook: "notebook";
|
|
266
266
|
}>;
|
|
267
267
|
description: z.ZodString;
|
|
@@ -597,9 +597,9 @@ export declare const CortexChatPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
597
597
|
fileType: z.ZodEnum<{
|
|
598
598
|
output: "output";
|
|
599
599
|
summary: "summary";
|
|
600
|
-
script: "script";
|
|
601
|
-
figure: "figure";
|
|
602
600
|
log: "log";
|
|
601
|
+
figure: "figure";
|
|
602
|
+
script: "script";
|
|
603
603
|
notebook: "notebook";
|
|
604
604
|
}>;
|
|
605
605
|
description: z.ZodString;
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,9 @@ export type { ProvenanceCollector } from "./provenance/collector.js";
|
|
|
36
36
|
export { UnavailableSessionPagePublisher } from "./tools/report-session/session-page-publisher.js";
|
|
37
37
|
export type { MakeSessionPagePublisher, SessionPagePublisher, SessionPageMintResult } from "./tools/report-session/session-page-publisher.js";
|
|
38
38
|
export type { ResolvePageUrl, SessionPageAccess } from "./tools/report-session/index.js";
|
|
39
|
+
export { bindReadExport, bindSessionEmit } from "./provenance/seam.js";
|
|
40
|
+
export type { ProvenanceExport, ProvenanceSeam, RunProvenanceEvent, SessionProvenanceEvent } from "./provenance/seam.js";
|
|
41
|
+
export type { DerivationSource } from "./state/report-session-state.js";
|
|
39
42
|
export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
|
|
40
43
|
export type { RunLauncher, LaunchOptions } from "./execution/run-launcher.js";
|
|
41
44
|
export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
|
|
@@ -140,7 +143,7 @@ export { deliverExecEvent, execEventTopic } from "./sandbox/deliver-exec-event.j
|
|
|
140
143
|
export { ExecEventMessageSchema } from "./sandbox/types.js";
|
|
141
144
|
export type { ExecEventMessage, DoneMarker } from "./sandbox/types.js";
|
|
142
145
|
export { registerExecuteAnalysis } from "./workflows/execute-analysis.js";
|
|
143
|
-
export type { ExecuteAnalysisDeps, ExecuteAnalysisInput, ExecuteAnalysisResult, RunObservation,
|
|
146
|
+
export type { ExecuteAnalysisDeps, ExecuteAnalysisInput, ExecuteAnalysisResult, RunObservation, RunStepObservation, StepRuntimeStatus, } from "./workflows/execute-analysis.js";
|
|
144
147
|
export { registerSandboxStep } from "./workflows/sandbox-step.js";
|
|
145
148
|
export type { SandboxStepDeps, SandboxStepInput, SandboxStepResult, SandboxAgentBuildContext } from "./workflows/sandbox-step.js";
|
|
146
149
|
export { DEFAULT_CLAIM_INVESTIGATION_CONFIG } from "./workflows/target-assessment/investigation/index.js";
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,14 @@ export { createNoopArtifactRegistry } from "./execution/noop-artifact-registry.j
|
|
|
47
47
|
// state. The URL seam of the eyes rides beside the publisher factory: a host whose
|
|
48
48
|
// browser cannot reach the workspace tree binds a served URL for the look.
|
|
49
49
|
export { UnavailableSessionPagePublisher } from "./tools/report-session/session-page-publisher.js";
|
|
50
|
+
// Seam: the one provenance surface. The embedder records the facts of a run and the acts of a
|
|
51
|
+
// session in the document of the analysis that it owns, and it gives the bytes of that document
|
|
52
|
+
// back for the report page. Thus the seam and its vocabulary belong on the front door: an embedder
|
|
53
|
+
// cannot type its realization without them. The two binders ride beside the types, because an
|
|
54
|
+
// embedder that calls `prepareChatTurn` or one tool factory directly binds the same total forms
|
|
55
|
+
// that the assembly binds. `DerivationSource` rides beside the event union, because the derivation
|
|
56
|
+
// event carries the chain of the derived table.
|
|
57
|
+
export { bindReadExport, bindSessionEmit } from "./provenance/seam.js";
|
|
50
58
|
// Run launching.
|
|
51
59
|
export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
|
|
52
60
|
// Seam: workflow-ledger reclamation — cancels and deletes an analysis's rows in
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provenance seam -- the one surface through which the harness tells an embedder what happened.
|
|
3
|
+
*
|
|
4
|
+
* Three duties answer one host concern. A durable run states the facts of its own lifecycle. A session
|
|
5
|
+
* states the acts that make a report. The report page needs the signed document of the analysis back, as
|
|
6
|
+
* bytes. The embedder holds that document, thus the harness declares the vocabulary and the seam alone,
|
|
7
|
+
* and it keeps no ledger of its own.
|
|
8
|
+
*
|
|
9
|
+
* One surface carries the three, and each member is optional alone. Thus a composition binds the members
|
|
10
|
+
* that it records, and the harness reads an absent member as absence and never as an error. The run emit
|
|
11
|
+
* takes the `RunSession` of the run, because a durable run carries one and a persisting realization needs
|
|
12
|
+
* its credential. A session act carries no session, thus the session emit takes the event alone.
|
|
13
|
+
*
|
|
14
|
+
* Both emit members are fire-and-forget. Neither gives a result, and the harness reads none. A site emits
|
|
15
|
+
* after its act lands, thus a refused act and a failed act each emit nothing and the record states what
|
|
16
|
+
* happened.
|
|
17
|
+
*/
|
|
18
|
+
import type { RunSession } from "../auth/types.js";
|
|
19
|
+
import type { Logger } from "../lib/logger.js";
|
|
20
|
+
import type { ProvenanceExport } from "../report-render/provenance-data.js";
|
|
21
|
+
import type { AuthoringBlock } from "../report-model/authoring-grammar.js";
|
|
22
|
+
import type { ExecuteAnalysisFinalStatus } from "../workflows/execute-analysis.js";
|
|
23
|
+
export type { ProvenanceExport } from "../report-render/provenance-data.js";
|
|
24
|
+
/**
|
|
25
|
+
* A run-lifecycle provenance observation handed to an optional host observer.
|
|
26
|
+
* Harness-owned plain union — the harness stays tsprov-free and bus-free; the
|
|
27
|
+
* host maps these execution facts onto its own ledger vocabulary.
|
|
28
|
+
*
|
|
29
|
+
* Every `atMs` is epoch milliseconds read via `await DBOS.now()`, a checkpointed
|
|
30
|
+
* step: a body re-executed by DBOS recovery reads the recorded value, so a
|
|
31
|
+
* re-emitted event carries the identical timestamp and merges on the host's
|
|
32
|
+
* ledger without a value conflict. Never source these from a wall clock
|
|
33
|
+
* (`Date.now()`) — that would diverge across replays and defeat the merge.
|
|
34
|
+
*
|
|
35
|
+
* `run_completed.durationMs` is the terminal `atMs` minus the `run_started`
|
|
36
|
+
* `atMs` (both `DBOS.now()` reads) — the true workflow-observed run span.
|
|
37
|
+
*
|
|
38
|
+
* `step_completed` fires once at EVERY scheduler-loop settlement — the only site
|
|
39
|
+
* that observes every executed step (registration sees only artifact-producing
|
|
40
|
+
* steps, and a child cannot observe its own parent-driven cancel). Steps that
|
|
41
|
+
* were never dispatched (dependents of a failed sibling) emit nothing by design;
|
|
42
|
+
* the run's terminal status carries that outcome. `status` maps the settlement
|
|
43
|
+
* outcome: `complete` → `"completed"`, `canceled` → `"canceled"`,
|
|
44
|
+
* `failed`/`blocked`/child-error → `"failed"`.
|
|
45
|
+
*
|
|
46
|
+
* `run_completed` fires at BOTH terminal boundaries (success and failure); the
|
|
47
|
+
* `status` field distinguishes them.
|
|
48
|
+
*/
|
|
49
|
+
export type RunProvenanceEvent = {
|
|
50
|
+
type: "run_started";
|
|
51
|
+
analysisId: string;
|
|
52
|
+
runId: string;
|
|
53
|
+
planSummary: string;
|
|
54
|
+
stepCount: number;
|
|
55
|
+
atMs: number;
|
|
56
|
+
} | {
|
|
57
|
+
type: "step_completed";
|
|
58
|
+
analysisId: string;
|
|
59
|
+
runId: string;
|
|
60
|
+
stepId: string;
|
|
61
|
+
/** Settlement outcome mapped to a terminal step status. */
|
|
62
|
+
status: "completed" | "failed" | "canceled";
|
|
63
|
+
/** The child's durable execution duration; absent when the child settled by throwing. */
|
|
64
|
+
durationMs?: number;
|
|
65
|
+
atMs: number;
|
|
66
|
+
} | {
|
|
67
|
+
type: "run_completed";
|
|
68
|
+
analysisId: string;
|
|
69
|
+
runId: string;
|
|
70
|
+
/**
|
|
71
|
+
* The body's terminal status. Both boundary sites resolve it through `deriveFinalStatus`,
|
|
72
|
+
* which records a budget pause as `"canceled"` — so `"suspended_insufficient_funds"` (a
|
|
73
|
+
* `RunStatus` member) is never emitted here and is deliberately absent from this narrower
|
|
74
|
+
* `ExecuteAnalysisFinalStatus`-minus-`"running"` set.
|
|
75
|
+
*/
|
|
76
|
+
status: Exclude<ExecuteAnalysisFinalStatus, "running">;
|
|
77
|
+
atMs: number;
|
|
78
|
+
/** `atMs − run_started.atMs`: the workflow-observed run span in ms. */
|
|
79
|
+
durationMs: number;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* One act on one block of a draft.
|
|
83
|
+
*
|
|
84
|
+
* The kind rides beside the id, because an id alone tells a reader nothing about what the act touched.
|
|
85
|
+
* The kind is the one that the document holds after the act: a change is permitted to change the kind,
|
|
86
|
+
* and a remove states the kind that the draft carried before it.
|
|
87
|
+
*/
|
|
88
|
+
type BlockAct = {
|
|
89
|
+
analysisId: string;
|
|
90
|
+
threadId: string;
|
|
91
|
+
blockId: string;
|
|
92
|
+
blockKind: AuthoringBlock["kind"];
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* One observation of a session.
|
|
96
|
+
*
|
|
97
|
+
* The union names one event for each act: the start of a session, the four block operations, the title,
|
|
98
|
+
* the derivation, the preview, and the record. Each event carries the analysis and the thread, thus a
|
|
99
|
+
* consumer places it with no lookup. The title sits on the document, thus the title event names no block.
|
|
100
|
+
*
|
|
101
|
+
* Each member carries what its own site holds at the moment that the act lands, and nothing more. The
|
|
102
|
+
* derivation carries the chain of the table, because the site pins it and a consumer cannot rebuild it
|
|
103
|
+
* from the path. The preview carries the page and the hash of the draft that it shows, thus a consumer
|
|
104
|
+
* ties a page to the document that made it.
|
|
105
|
+
*
|
|
106
|
+
* The `create-session` event names the kind of the new session, and it names the parent thread of a child
|
|
107
|
+
* session. An analysis holds sessions of more than one kind, and the thread id alone says neither. Thus
|
|
108
|
+
* the document of the analysis tells the whole tree of the sessions, and a reader walks it with no second
|
|
109
|
+
* read of the seam. A root session carries no parent, thus `parentThreadId` is absent on it.
|
|
110
|
+
*/
|
|
111
|
+
export type SessionProvenanceEvent = {
|
|
112
|
+
type: "create-session";
|
|
113
|
+
analysisId: string;
|
|
114
|
+
threadId: string;
|
|
115
|
+
sessionKind: "conversation" | "report";
|
|
116
|
+
parentThreadId?: string;
|
|
117
|
+
} | ({
|
|
118
|
+
type: "add-block";
|
|
119
|
+
} & BlockAct) | ({
|
|
120
|
+
type: "change-block";
|
|
121
|
+
} & BlockAct) | ({
|
|
122
|
+
type: "remove-block";
|
|
123
|
+
} & BlockAct) | ({
|
|
124
|
+
type: "move-block";
|
|
125
|
+
} & BlockAct) | {
|
|
126
|
+
type: "set-title";
|
|
127
|
+
analysisId: string;
|
|
128
|
+
threadId: string;
|
|
129
|
+
title: string;
|
|
130
|
+
} | {
|
|
131
|
+
type: "run-derivation";
|
|
132
|
+
analysisId: string;
|
|
133
|
+
threadId: string;
|
|
134
|
+
outputPath: string;
|
|
135
|
+
outputHash: string;
|
|
136
|
+
scriptHash: string;
|
|
137
|
+
sources: ReadonlyArray<{
|
|
138
|
+
path: string;
|
|
139
|
+
hash: string;
|
|
140
|
+
}>;
|
|
141
|
+
} | {
|
|
142
|
+
type: "preview";
|
|
143
|
+
analysisId: string;
|
|
144
|
+
threadId: string;
|
|
145
|
+
pagePath: string;
|
|
146
|
+
documentHash: string;
|
|
147
|
+
} | {
|
|
148
|
+
type: "record-version";
|
|
149
|
+
analysisId: string;
|
|
150
|
+
threadId: string;
|
|
151
|
+
versionId: string;
|
|
152
|
+
replaced: boolean;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* The provenance seam of the harness. An embedder realizes the members that it records, and the harness
|
|
156
|
+
* never branches on which realization is bound.
|
|
157
|
+
*/
|
|
158
|
+
export type ProvenanceSeam = {
|
|
159
|
+
/**
|
|
160
|
+
* The run engine emits the facts of one run here.
|
|
161
|
+
*
|
|
162
|
+
* Synchronous by signature (`void`, never a promise): a host that needs input and output dispatches
|
|
163
|
+
* the work rather than making a workflow body wait behind it. The `RunSession` rides from the durable
|
|
164
|
+
* workflow input, because a persisting realization needs the credential, the scope, and the identity
|
|
165
|
+
* of the run. A realization can ignore the parameter.
|
|
166
|
+
*/
|
|
167
|
+
readonly emitRunEvent?: (event: RunProvenanceEvent, session: RunSession) => void;
|
|
168
|
+
/**
|
|
169
|
+
* A session emits one act here. A session act runs under no authorized run, thus this member takes
|
|
170
|
+
* the event alone. It is synchronous by signature for the same reason as the run emit.
|
|
171
|
+
*/
|
|
172
|
+
readonly emitSessionEvent?: (event: SessionProvenanceEvent) => void;
|
|
173
|
+
/**
|
|
174
|
+
* The page staging reads the signed document of one analysis here.
|
|
175
|
+
*
|
|
176
|
+
* The member can be async, because a source can read a file or ask a service. It gives the bytes and
|
|
177
|
+
* the attestation, or `undefined`. Absence is a normal result, not a fault: the page then carries no
|
|
178
|
+
* provenance asset, and each other part of the render is what it was.
|
|
179
|
+
*/
|
|
180
|
+
readonly readExport?: (analysisId: string) => ProvenanceExport | undefined | Promise<ProvenanceExport | undefined>;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Bind the optional session emit and the logger of one site into a total emit.
|
|
184
|
+
*
|
|
185
|
+
* An absent member gives a call that does nothing. Thus a site emits where its act lands with no test of
|
|
186
|
+
* its own, and absence stays a normal condition.
|
|
187
|
+
*
|
|
188
|
+
* The harness is host-agnostic, and it cannot assume that a host callback is total. Thus a throw of the
|
|
189
|
+
* realization reaches the log and stops there: an observation is a record of an act, and a defect in it
|
|
190
|
+
* must never undo the act that already landed.
|
|
191
|
+
*/
|
|
192
|
+
export declare function bindSessionEmit(seam: ProvenanceSeam | undefined, logger: Logger): (event: SessionProvenanceEvent) => void;
|
|
193
|
+
/**
|
|
194
|
+
* Bind the optional document read and the logger of one site into a total read.
|
|
195
|
+
*
|
|
196
|
+
* An absent member gives a call that answers absence. Thus a site asks at its one place with no test of
|
|
197
|
+
* its own, and an unbound composition needs no branch.
|
|
198
|
+
*
|
|
199
|
+
* A throw of the realization reaches the log and becomes absence, for the same reason that a throw of an
|
|
200
|
+
* emit stops at the log: the provenance of the page is an addition to the report, and a defect in it must
|
|
201
|
+
* never cost the render.
|
|
202
|
+
*/
|
|
203
|
+
export declare function bindReadExport(seam: ProvenanceSeam | undefined, logger: Logger): (analysisId: string) => Promise<ProvenanceExport | undefined>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provenance seam -- the one surface through which the harness tells an embedder what happened.
|
|
3
|
+
*
|
|
4
|
+
* Three duties answer one host concern. A durable run states the facts of its own lifecycle. A session
|
|
5
|
+
* states the acts that make a report. The report page needs the signed document of the analysis back, as
|
|
6
|
+
* bytes. The embedder holds that document, thus the harness declares the vocabulary and the seam alone,
|
|
7
|
+
* and it keeps no ledger of its own.
|
|
8
|
+
*
|
|
9
|
+
* One surface carries the three, and each member is optional alone. Thus a composition binds the members
|
|
10
|
+
* that it records, and the harness reads an absent member as absence and never as an error. The run emit
|
|
11
|
+
* takes the `RunSession` of the run, because a durable run carries one and a persisting realization needs
|
|
12
|
+
* its credential. A session act carries no session, thus the session emit takes the event alone.
|
|
13
|
+
*
|
|
14
|
+
* Both emit members are fire-and-forget. Neither gives a result, and the harness reads none. A site emits
|
|
15
|
+
* after its act lands, thus a refused act and a failed act each emit nothing and the record states what
|
|
16
|
+
* happened.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Bind the optional session emit and the logger of one site into a total emit.
|
|
20
|
+
*
|
|
21
|
+
* An absent member gives a call that does nothing. Thus a site emits where its act lands with no test of
|
|
22
|
+
* its own, and absence stays a normal condition.
|
|
23
|
+
*
|
|
24
|
+
* The harness is host-agnostic, and it cannot assume that a host callback is total. Thus a throw of the
|
|
25
|
+
* realization reaches the log and stops there: an observation is a record of an act, and a defect in it
|
|
26
|
+
* must never undo the act that already landed.
|
|
27
|
+
*/
|
|
28
|
+
export function bindSessionEmit(seam, logger) {
|
|
29
|
+
const emit = seam?.emitSessionEvent;
|
|
30
|
+
if (emit === undefined) {
|
|
31
|
+
return () => undefined;
|
|
32
|
+
}
|
|
33
|
+
return (event) => {
|
|
34
|
+
try {
|
|
35
|
+
emit(event);
|
|
36
|
+
}
|
|
37
|
+
catch (cause) {
|
|
38
|
+
logger.error("the session emit of the provenance seam threw", {
|
|
39
|
+
analysisId: event.analysisId,
|
|
40
|
+
threadId: event.threadId,
|
|
41
|
+
event: event.type,
|
|
42
|
+
...logger.errorFields(cause),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Bind the optional document read and the logger of one site into a total read.
|
|
49
|
+
*
|
|
50
|
+
* An absent member gives a call that answers absence. Thus a site asks at its one place with no test of
|
|
51
|
+
* its own, and an unbound composition needs no branch.
|
|
52
|
+
*
|
|
53
|
+
* A throw of the realization reaches the log and becomes absence, for the same reason that a throw of an
|
|
54
|
+
* emit stops at the log: the provenance of the page is an addition to the report, and a defect in it must
|
|
55
|
+
* never cost the render.
|
|
56
|
+
*/
|
|
57
|
+
export function bindReadExport(seam, logger) {
|
|
58
|
+
const read = seam?.readExport;
|
|
59
|
+
if (read === undefined) {
|
|
60
|
+
return () => Promise.resolve(undefined);
|
|
61
|
+
}
|
|
62
|
+
return async (analysisId) => {
|
|
63
|
+
try {
|
|
64
|
+
return await read(analysisId);
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
logger.error("the document read of the provenance seam threw", { analysisId, ...logger.errorFields(cause) });
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|