@inflexa-ai/harness 0.19.1 → 0.20.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 +7 -0
- package/dist/agents/report-session-agent.js +2 -1
- package/dist/auth/local-run-authorizer.d.ts +3 -2
- package/dist/auth/local-run-authorizer.js +4 -2
- package/dist/execution/run-authorizer.d.ts +18 -0
- package/dist/execution/run-canceler.d.ts +67 -0
- package/dist/execution/run-canceler.js +130 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -1
- package/dist/literature/sources/pubmed.js +2 -1
- package/dist/report-render/assets.d.ts +42 -0
- package/dist/report-render/assets.js +61 -0
- package/dist/report-render/chart.js +31 -10
- package/dist/report-render/design.d.ts +120 -0
- package/dist/report-render/design.js +808 -0
- package/dist/report-render/page.d.ts +32 -115
- package/dist/report-render/page.js +130 -273
- package/dist/report-render/render.js +34 -11
- package/dist/report-render/views/chart-view.d.ts +6 -3
- package/dist/report-render/views/chart-view.js +10 -5
- package/dist/report-render/views/page-view.d.ts +19 -8
- package/dist/report-render/views/page-view.js +31 -11
- package/dist/report-render/views/prose.d.ts +1 -0
- package/dist/report-render/views/prose.js +7 -13
- package/dist/report-render/views/references-view.js +5 -5
- package/dist/report-render/views/values.d.ts +15 -6
- package/dist/report-render/views/values.js +29 -10
- package/dist/runtime/assemble.d.ts +10 -0
- package/dist/runtime/assemble.js +1 -0
- package/dist/sandbox/k8s-client.js +12 -1
- package/dist/sandbox/types.d.ts +4 -0
- package/dist/state/analyses.d.ts +3 -26
- package/dist/state/analyses.js +6 -64
- package/dist/state/index.d.ts +2 -2
- package/dist/state/index.js +2 -2
- package/dist/state/init.js +3 -3
- package/dist/state/runs.d.ts +10 -0
- package/dist/state/runs.js +21 -0
- package/dist/tasks/data-profile.d.ts +7 -0
- package/dist/tasks/data-profile.js +14 -0
- package/dist/tools/approval/gateway.d.ts +8 -1
- package/dist/tools/approval/gateway.js +9 -2
- package/dist/tools/approval/queries.d.ts +9 -2
- package/dist/tools/approval/queries.js +12 -4
- package/dist/tools/report-session/index.d.ts +1 -1
- package/dist/tools/report-session/index.js +1 -1
- package/dist/tools/report-session/preview-report.d.ts +11 -1
- package/dist/tools/report-session/preview-report.js +35 -12
- package/dist/workflows/sandbox-step.d.ts +7 -0
- package/dist/workflows/sandbox-step.js +19 -2
- package/package.json +5 -1
|
@@ -30,6 +30,7 @@ import type { ThreadStore } from "../memory/thread-store.js";
|
|
|
30
30
|
import type { ReferenceResolver } from "../report-model/reference-resolver.js";
|
|
31
31
|
import type { ReportVersionStore } from "../state/report-versions.js";
|
|
32
32
|
import type { ReportSessionStateGateway } from "../tools/report-authoring/authoring-tools.js";
|
|
33
|
+
import { type ResolvePageAsset } from "../tools/report-session/index.js";
|
|
33
34
|
/** Canonical agent id -- distinct from the id of the old templating agent. */
|
|
34
35
|
export declare const REPORT_SESSION_AGENT_ID: "report-session";
|
|
35
36
|
/**
|
|
@@ -65,6 +66,12 @@ export interface ReportSessionAgentDeps {
|
|
|
65
66
|
analysisId: string;
|
|
66
67
|
auth: AuthContext;
|
|
67
68
|
}) => ReferenceResolver;
|
|
69
|
+
/**
|
|
70
|
+
* Page-asset lookup -- the preview tool maps the module specifier of a manifest
|
|
71
|
+
* entry onto a file on disk. Omitted falls back to the module resolution of the
|
|
72
|
+
* installation of the harness.
|
|
73
|
+
*/
|
|
74
|
+
readonly resolvePageAsset?: ResolvePageAsset;
|
|
68
75
|
/** Operational logging seam; omitted falls back to no-op. */
|
|
69
76
|
readonly logger?: Logger;
|
|
70
77
|
}
|
|
@@ -33,7 +33,7 @@ export const REPORT_SESSION_AGENT_ID = "report-session";
|
|
|
33
33
|
const REPORT_SESSION_MAX_ITERATIONS = 50;
|
|
34
34
|
/** Build the report `AgentDefinition` with every tool bound to its deps. */
|
|
35
35
|
export function createReportSessionAgent(deps) {
|
|
36
|
-
const { model, pool, embedding, workspaceFs, gateway, resolveWorkspaceRoot, store, threads, chrome, makeResolver, logger } = deps;
|
|
36
|
+
const { model, pool, embedding, workspaceFs, gateway, resolveWorkspaceRoot, store, threads, chrome, makeResolver, resolvePageAsset, logger } = deps;
|
|
37
37
|
const authoring = createReportAuthoringTools(gateway);
|
|
38
38
|
const tools = [
|
|
39
39
|
// The read surface toward the analysis. It roams the tree read-only.
|
|
@@ -59,6 +59,7 @@ export function createReportSessionAgent(deps) {
|
|
|
59
59
|
gateway,
|
|
60
60
|
resolveWorkspaceRoot,
|
|
61
61
|
...(makeResolver ? { makeResolver } : {}),
|
|
62
|
+
...(resolvePageAsset ? { resolvePageAsset } : {}),
|
|
62
63
|
...(logger ? { logger } : {}),
|
|
63
64
|
}),
|
|
64
65
|
// The eyes tool. It opens the rendered page in headless Chrome.
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* LocalRunAuthorizer — the OSS realization of the `RunAuthorizer` seam.
|
|
3
3
|
*
|
|
4
4
|
* Issues a durable `RunSession` straight from the async-edge input: no remote
|
|
5
|
-
* authorization, no credential read. It owns nothing revocable
|
|
6
|
-
*
|
|
5
|
+
* authorization, no credential read. It owns nothing revocable (it mints no
|
|
6
|
+
* jti), so `revoke` and `revokeByJti` are no-ops. The opaque caller `auth` is
|
|
7
|
+
* forwarded untouched — local code never
|
|
7
8
|
* inspects it (no `getAuth`).
|
|
8
9
|
*/
|
|
9
10
|
import type { RunAuthorizer } from "../execution/run-authorizer.js";
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* LocalRunAuthorizer — the OSS realization of the `RunAuthorizer` seam.
|
|
3
3
|
*
|
|
4
4
|
* Issues a durable `RunSession` straight from the async-edge input: no remote
|
|
5
|
-
* authorization, no credential read. It owns nothing revocable
|
|
6
|
-
*
|
|
5
|
+
* authorization, no credential read. It owns nothing revocable (it mints no
|
|
6
|
+
* jti), so `revoke` and `revokeByJti` are no-ops. The opaque caller `auth` is
|
|
7
|
+
* forwarded untouched — local code never
|
|
7
8
|
* inspects it (no `getAuth`).
|
|
8
9
|
*/
|
|
9
10
|
export function createLocalRunAuthorizer() {
|
|
@@ -19,5 +20,6 @@ export function createLocalRunAuthorizer() {
|
|
|
19
20
|
return { runSession, ownsMandate: false }; // oss-core-managed-ok
|
|
20
21
|
},
|
|
21
22
|
async revoke() { },
|
|
23
|
+
async revokeByJti() { },
|
|
22
24
|
};
|
|
23
25
|
}
|
|
@@ -15,6 +15,16 @@
|
|
|
15
15
|
* realization injected at the composition root.
|
|
16
16
|
*/
|
|
17
17
|
import type { AuthContext, Provenance, RunFrame, RunSession, Scope } from "../auth/types.js";
|
|
18
|
+
/**
|
|
19
|
+
* Locates a run credential when no `RunAuthorization` exists: the persisted
|
|
20
|
+
* jti from `cortex_runs` plus the revoking caller's opaque `auth` — the sole
|
|
21
|
+
* carrier of credential/org behind a session, downcast only by managed
|
|
22
|
+
* realizations.
|
|
23
|
+
*/
|
|
24
|
+
export interface RevokeByJtiRef {
|
|
25
|
+
readonly jti: string;
|
|
26
|
+
readonly auth: AuthContext;
|
|
27
|
+
}
|
|
18
28
|
/** What an async edge knows when it authorizes a run: the opaque caller auth,
|
|
19
29
|
* the scope + provenance to stamp on the run, and the run frame to mint for. */
|
|
20
30
|
export interface AuthorizeRunInput {
|
|
@@ -40,4 +50,12 @@ export interface RunAuthorization {
|
|
|
40
50
|
export interface RunAuthorizer {
|
|
41
51
|
authorize(input: AuthorizeRunInput): Promise<RunAuthorization>;
|
|
42
52
|
revoke(authorization: RunAuthorization, reason: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Out-of-band revocation for paths holding no `RunAuthorization`. Normal
|
|
55
|
+
* terminal paths use `revoke(authorization, reason)` under the run
|
|
56
|
+
* credential; `revokeByJti` covers external cancellation, where the run
|
|
57
|
+
* credential's JWT was never persisted and only its jti survives on
|
|
58
|
+
* `cortex_runs`. Local/OSS authorizers no-op — they mint no jti.
|
|
59
|
+
*/
|
|
60
|
+
revokeByJti(ref: RevokeByJtiRef, reason: string): Promise<void>;
|
|
43
61
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunCanceler — the encapsulated external-cancel path for an `executeAnalysis` run.
|
|
3
|
+
*
|
|
4
|
+
* A cancelled DBOS workflow can never execute another step, so the body's own
|
|
5
|
+
* terminal block (`collectAndComplete`) is unreachable on external cancel and
|
|
6
|
+
* nothing converges: the run row stays `running` (blocking same-plan relaunch
|
|
7
|
+
* through `queryActiveRun`'s active-status dedup), pending step rows never
|
|
8
|
+
* sweep, the running charge never closes, and the run mandate is never
|
|
9
|
+
* revoked. This module is the host-side counterpart: cancel the engine's
|
|
10
|
+
* parent + children, then converge each ledger the workflow would have.
|
|
11
|
+
*
|
|
12
|
+
* The engine capability stays internal — the production default is the
|
|
13
|
+
* purger's `DBOSClient`-backed child-cascading cancel, which needs no launched
|
|
14
|
+
* engine (see `dbos-workflow-purger.ts` for why the static `DBOS` facade is
|
|
15
|
+
* the wrong tool here). `cancelWorkflows` is an optional test seam over it.
|
|
16
|
+
*
|
|
17
|
+
* No stream writes: `DBOS.writeStream` is body-only, so hosts synthesize the
|
|
18
|
+
* terminal frame read-side.
|
|
19
|
+
*/
|
|
20
|
+
import type { Pool } from "pg";
|
|
21
|
+
import type { AgentSession } from "../auth/types.js";
|
|
22
|
+
import type { RunCharge } from "../billing/run-charge.js";
|
|
23
|
+
import type { Logger } from "../lib/logger.js";
|
|
24
|
+
import type { RunStatus } from "../state/schema.js";
|
|
25
|
+
import type { RunAuthorizer } from "./run-authorizer.js";
|
|
26
|
+
export declare class UnknownRunError extends Error {
|
|
27
|
+
readonly runId: string;
|
|
28
|
+
constructor(runId: string);
|
|
29
|
+
}
|
|
30
|
+
export interface CancelRunResult {
|
|
31
|
+
readonly runId: string;
|
|
32
|
+
/** The DBOS parent workflow id — equal to `runId` by the launch contract
|
|
33
|
+
* (`execute_analysis` launches with `workflowId: runId`). */
|
|
34
|
+
readonly workflowId: string;
|
|
35
|
+
readonly outcome: "canceled" | "already_terminal";
|
|
36
|
+
/**
|
|
37
|
+
* The run row's status after the cancel. `canceled` when this cancel won
|
|
38
|
+
* the row; the run's own terminal status when it completed concurrently
|
|
39
|
+
* (the conditional write refuses to clobber it).
|
|
40
|
+
*/
|
|
41
|
+
readonly finalStatus: RunStatus;
|
|
42
|
+
/** What actually converged. `mandate` is vacuously true for a row with no
|
|
43
|
+
* persisted jti — nothing to revoke, so hosts alert on any false flag. */
|
|
44
|
+
readonly converged: {
|
|
45
|
+
readonly steps: boolean;
|
|
46
|
+
readonly charge: boolean;
|
|
47
|
+
readonly mandate: boolean;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface RunCancelerDeps {
|
|
51
|
+
readonly pool: Pool;
|
|
52
|
+
readonly runCharge: RunCharge;
|
|
53
|
+
readonly runAuthorizer: RunAuthorizer;
|
|
54
|
+
readonly logger?: Logger;
|
|
55
|
+
/** Test seam over the engine cancel. Production default: the workflow
|
|
56
|
+
* purger's `DBOSClient`-backed cancel, child-cascading and launch-free. */
|
|
57
|
+
readonly cancelWorkflows?: (workflowIds: readonly string[]) => Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
export interface RunCanceler {
|
|
60
|
+
/**
|
|
61
|
+
* Cancel the run's workflows and converge its ledgers. Rejects when the
|
|
62
|
+
* engine cancel itself fails (retry-safe: nothing converged) or the run is
|
|
63
|
+
* unknown; convergence-phase failures resolve with their flag false.
|
|
64
|
+
*/
|
|
65
|
+
cancel(runId: string, session: AgentSession): Promise<CancelRunResult>;
|
|
66
|
+
}
|
|
67
|
+
export declare function createRunCanceler(deps: RunCancelerDeps): RunCanceler;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunCanceler — the encapsulated external-cancel path for an `executeAnalysis` run.
|
|
3
|
+
*
|
|
4
|
+
* A cancelled DBOS workflow can never execute another step, so the body's own
|
|
5
|
+
* terminal block (`collectAndComplete`) is unreachable on external cancel and
|
|
6
|
+
* nothing converges: the run row stays `running` (blocking same-plan relaunch
|
|
7
|
+
* through `queryActiveRun`'s active-status dedup), pending step rows never
|
|
8
|
+
* sweep, the running charge never closes, and the run mandate is never
|
|
9
|
+
* revoked. This module is the host-side counterpart: cancel the engine's
|
|
10
|
+
* parent + children, then converge each ledger the workflow would have.
|
|
11
|
+
*
|
|
12
|
+
* The engine capability stays internal — the production default is the
|
|
13
|
+
* purger's `DBOSClient`-backed child-cascading cancel, which needs no launched
|
|
14
|
+
* engine (see `dbos-workflow-purger.ts` for why the static `DBOS` facade is
|
|
15
|
+
* the wrong tool here). `cancelWorkflows` is an optional test seam over it.
|
|
16
|
+
*
|
|
17
|
+
* No stream writes: `DBOS.writeStream` is body-only, so hosts synthesize the
|
|
18
|
+
* terminal frame read-side.
|
|
19
|
+
*/
|
|
20
|
+
import { createNoopLogger } from "../lib/console-logger.js";
|
|
21
|
+
import { unwrapOrThrow } from "../lib/result.js";
|
|
22
|
+
import { markRunCanceledIfActive, queryRun } from "../state/runs.js";
|
|
23
|
+
import { queryStepsByRun, sweepPendingStepExecutions } from "../state/step-executions.js";
|
|
24
|
+
import { createDbosWorkflowPurger } from "./dbos-workflow-purger.js";
|
|
25
|
+
/** The reason stamped on the run row and the revoke — the same literal
|
|
26
|
+
* `collectAndComplete` records for a cancel it observes from inside. */
|
|
27
|
+
const EXTERNAL_CANCEL_REASON = "external_cancel";
|
|
28
|
+
export class UnknownRunError extends Error {
|
|
29
|
+
runId;
|
|
30
|
+
constructor(runId) {
|
|
31
|
+
super(`no run exists for id ${runId}`);
|
|
32
|
+
this.runId = runId;
|
|
33
|
+
this.name = "UnknownRunError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const TERMINAL = new Set(["completed", "partial", "failed", "canceled"]);
|
|
37
|
+
export function createRunCanceler(deps) {
|
|
38
|
+
const { pool, runCharge, runAuthorizer } = deps;
|
|
39
|
+
const logger = (deps.logger ?? createNoopLogger()).named("run-canceler");
|
|
40
|
+
// Lazy so a canceler built for a host that never cancels constructs no client;
|
|
41
|
+
// one purger per factory (its client registers pool handlers — see its comment).
|
|
42
|
+
let engineCancel = deps.cancelWorkflows;
|
|
43
|
+
const cancelWorkflows = async (workflowIds) => {
|
|
44
|
+
engineCancel ??= (() => {
|
|
45
|
+
const purger = createDbosWorkflowPurger({ pool, logger });
|
|
46
|
+
return async (ids) => {
|
|
47
|
+
unwrapOrThrow(await purger.cancel([...ids]));
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
await engineCancel(workflowIds);
|
|
51
|
+
};
|
|
52
|
+
/** Persisted child workflow ids of steps not yet completed. */
|
|
53
|
+
const incompleteChildIds = async (runId) => unwrapOrThrow(await queryStepsByRun(pool, runId)).flatMap((step) => step.childWorkflowId !== null && step.completedAt === null ? [step.childWorkflowId] : []);
|
|
54
|
+
return {
|
|
55
|
+
async cancel(runId, session) {
|
|
56
|
+
const row = unwrapOrThrow(await queryRun(pool, runId));
|
|
57
|
+
if (row === null)
|
|
58
|
+
throw new UnknownRunError(runId);
|
|
59
|
+
const workflowId = runId;
|
|
60
|
+
if (TERMINAL.has(row.status)) {
|
|
61
|
+
return {
|
|
62
|
+
runId,
|
|
63
|
+
workflowId,
|
|
64
|
+
outcome: "already_terminal",
|
|
65
|
+
finalStatus: row.status,
|
|
66
|
+
converged: { steps: false, charge: false, mandate: false },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// The child-cascading cancel walks the engine's own parent ledger, which
|
|
70
|
+
// has a row the moment a child starts — that covers a child whose
|
|
71
|
+
// mark-running step has not yet committed its child_workflow_id. The
|
|
72
|
+
// persisted ids and the post-cancel re-query are belt-and-braces over
|
|
73
|
+
// both ledgers' lag.
|
|
74
|
+
const knownChildren = await incompleteChildIds(runId);
|
|
75
|
+
await cancelWorkflows([workflowId, ...knownChildren]);
|
|
76
|
+
try {
|
|
77
|
+
const late = (await incompleteChildIds(runId)).filter((id) => !knownChildren.includes(id));
|
|
78
|
+
if (late.length > 0)
|
|
79
|
+
await cancelWorkflows(late);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
logger.error("late-child cancel sweep failed", { runId, ...logger.errorFields(err) });
|
|
83
|
+
}
|
|
84
|
+
// Convergence — each phase isolated so one failure skips nothing after it.
|
|
85
|
+
let transitioned = false;
|
|
86
|
+
try {
|
|
87
|
+
transitioned = unwrapOrThrow(await markRunCanceledIfActive(pool, runId, EXTERNAL_CANCEL_REASON));
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
logger.error("markRunCanceledIfActive failed", { runId, ...logger.errorFields(err) });
|
|
91
|
+
}
|
|
92
|
+
let steps = false;
|
|
93
|
+
try {
|
|
94
|
+
unwrapOrThrow(await sweepPendingStepExecutions(pool, runId));
|
|
95
|
+
steps = true;
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
logger.error("pending-step sweep failed", { runId, ...logger.errorFields(err) });
|
|
99
|
+
}
|
|
100
|
+
// Best-effort: the billing authority self-heals (defensive open + stale
|
|
101
|
+
// reaper), so a failed close loses attribution only.
|
|
102
|
+
let charge = false;
|
|
103
|
+
try {
|
|
104
|
+
await runCharge.close({ analysisId: row.analysisId, runId, reason: "canceled", session });
|
|
105
|
+
charge = true;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
logger.error("running-charge close failed", { runId, ...logger.errorFields(err) });
|
|
109
|
+
}
|
|
110
|
+
let mandate = row.mandateJti === null;
|
|
111
|
+
if (row.mandateJti !== null) {
|
|
112
|
+
try {
|
|
113
|
+
await runAuthorizer.revokeByJti({ jti: row.mandateJti, auth: session.auth }, EXTERNAL_CANCEL_REASON);
|
|
114
|
+
mandate = true;
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
logger.error("mandate revoke failed", { runId, jti: row.mandateJti, ...logger.errorFields(err) });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// No transition means the run reached a terminal state on its own (or the
|
|
121
|
+
// write failed) — report the row as it stands rather than claiming `canceled`.
|
|
122
|
+
let finalStatus = "canceled";
|
|
123
|
+
if (!transitioned) {
|
|
124
|
+
const reread = unwrapOrThrow(await queryRun(pool, runId));
|
|
125
|
+
finalStatus = reread?.status ?? "canceled";
|
|
126
|
+
}
|
|
127
|
+
return { runId, workflowId, outcome: "canceled", finalStatus, converged: { steps, charge, mandate } };
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
export { assembleCoreRuntime } from "./runtime/assemble.js";
|
|
8
8
|
export type { CoreRuntime, CoreRuntimeDeps, CoreWorkflowDeps, RegisteredWorkflows, ConversationAssemblyDeps, SandboxStepCallable, MakeReportReferenceResolver, ReportResolverScope, } from "./runtime/assemble.js";
|
|
9
9
|
export type { ReferenceResolver, ReportSnapshot, ResolvedValue } from "./report-model/reference-resolver.js";
|
|
10
|
+
export type { ResolvePageAsset } from "./tools/report-session/index.js";
|
|
10
11
|
export type { ThreadAgentResolver, UnregisteredThreadType } from "./runtime/assemble.js";
|
|
11
12
|
export { bootHarness } from "./runtime/boot.js";
|
|
12
13
|
export type { BootHarnessDeps, BootedHarness } from "./runtime/boot.js";
|
|
13
14
|
export { createConversationAgent } from "./agents/conversation-agent.js";
|
|
14
15
|
export type { ConversationAgentDeps } from "./agents/conversation-agent.js";
|
|
15
16
|
export { createLocalRunAuthorizer } from "./auth/local-run-authorizer.js";
|
|
16
|
-
export type { RunAuthorizer, AuthorizeRunInput, RunAuthorization } from "./execution/run-authorizer.js";
|
|
17
|
+
export type { RunAuthorizer, AuthorizeRunInput, RunAuthorization, RevokeByJtiRef } from "./execution/run-authorizer.js";
|
|
17
18
|
export { createNoopBillingResolver } from "./billing/noop-resolver.js";
|
|
18
19
|
export type { ResolveBilling, ResolvableSession, BillingMap, BillingHeaders, BillingFetchResult } from "./billing/resolver.js";
|
|
19
20
|
export { createNoopRunCharge } from "./billing/noop-run-charge.js";
|
|
@@ -31,6 +32,8 @@ export type { RunLauncher, LaunchOptions } from "./execution/run-launcher.js";
|
|
|
31
32
|
export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
|
|
32
33
|
export type { DbosWorkflowPurgerDeps } from "./execution/dbos-workflow-purger.js";
|
|
33
34
|
export type { WorkflowPurger } from "./execution/workflow-purger.js";
|
|
35
|
+
export { createRunCanceler, UnknownRunError } from "./execution/run-canceler.js";
|
|
36
|
+
export type { RunCanceler, RunCancelerDeps, CancelRunResult } from "./execution/run-canceler.js";
|
|
34
37
|
export { defineTool, isToolError } from "./tools/define-tool.js";
|
|
35
38
|
export type { Tool, ToolDefinition, ToolContext, ToolError } from "./tools/define-tool.js";
|
|
36
39
|
export { normalizeDetail, DETAIL_MAX_LENGTH } from "./loop/tool-detail.js";
|
|
@@ -137,7 +140,7 @@ export type { ValidatePlanOptions } from "./schemas/validate-plan.js";
|
|
|
137
140
|
export type { ValidationResult } from "./schemas/validate-plan.js";
|
|
138
141
|
export { upsertPlan, loadPlan } from "./state/plans.js";
|
|
139
142
|
export type { UpsertPlanInput } from "./state/plans.js";
|
|
140
|
-
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, queryRun, queryRunsByAnalysis, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
143
|
+
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
141
144
|
export type { InsertRunInput, RunReservation } from "./state/runs.js";
|
|
142
145
|
export { queryStepsByRun } from "./state/step-executions.js";
|
|
143
146
|
export type { CortexRunRow, StepExecutionRow, RunStatus } from "./state/schema.js";
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,11 @@ export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
|
|
|
31
31
|
// Seam: workflow-ledger reclamation — cancels and deletes an analysis's rows in
|
|
32
32
|
// the durability engine's own ledger, so no caller names an engine type.
|
|
33
33
|
export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
|
|
34
|
+
// Run cancellation — the encapsulated external-cancel path: engine cancel
|
|
35
|
+
// (parent + children) plus convergence of the run row, pending step rows,
|
|
36
|
+
// running charge, and run mandate. Hosts call this from their cancel route
|
|
37
|
+
// instead of hand-rolling `DBOS.cancelWorkflow` + partial cleanup.
|
|
38
|
+
export { createRunCanceler, UnknownRunError } from "./execution/run-canceler.js";
|
|
34
39
|
// Tool primitive.
|
|
35
40
|
export { defineTool, isToolError } from "./tools/define-tool.js";
|
|
36
41
|
// Tool-call-detail authoring aids for an embedder that contributes a host tool.
|
|
@@ -224,7 +229,7 @@ export { validatePlan } from "./schemas/validate-plan.js";
|
|
|
224
229
|
// caller-derived id and inserts-if-absent (the deterministic-id intake path);
|
|
225
230
|
// the run-state calls mirror the chat trigger's reserve → dedup → status flow.
|
|
226
231
|
export { upsertPlan, loadPlan } from "./state/plans.js";
|
|
227
|
-
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, queryRun, queryRunsByAnalysis, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
232
|
+
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
228
233
|
export { queryStepsByRun } from "./state/step-executions.js";
|
|
229
234
|
// Host-neutral reference-data catalog and shared installation receipts. Each
|
|
230
235
|
// artifact names the third-party upstream it is fetched from; embedders own all
|
|
@@ -11,7 +11,8 @@ const IdConvResponseSchema = z.object({
|
|
|
11
11
|
});
|
|
12
12
|
export function ncbiUrl(apiKey, base, params) {
|
|
13
13
|
const url = new URL(base);
|
|
14
|
-
|
|
14
|
+
// The URL must not include an empty key. NCBI rejects `api_key=` with HTTP 400.
|
|
15
|
+
if (apiKey)
|
|
15
16
|
url.searchParams.set("api_key", apiKey);
|
|
16
17
|
for (const [key, value] of Object.entries(params))
|
|
17
18
|
if (value !== undefined)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The asset manifest of the page.
|
|
3
|
+
*
|
|
4
|
+
* The page loads its chart runtime and its fonts from the sibling `assets/` directory. Thus each entry
|
|
5
|
+
* names the staged file and the module specifier of its source. The caller resolves each specifier against
|
|
6
|
+
* its own installation, and it copies the bytes beside the page.
|
|
7
|
+
*
|
|
8
|
+
* The module holds data alone. The renderer reads no file, thus the page stays a pure function of its
|
|
9
|
+
* inputs.
|
|
10
|
+
*/
|
|
11
|
+
/** The directory of the staged assets, relative to the page. */
|
|
12
|
+
export declare const ASSETS_DIR = "assets";
|
|
13
|
+
/**
|
|
14
|
+
* One staged asset. `file` is the basename under the assets directory, and `specifier` is the module
|
|
15
|
+
* specifier of the source file inside the installed package.
|
|
16
|
+
*/
|
|
17
|
+
export interface PageAsset {
|
|
18
|
+
readonly file: string;
|
|
19
|
+
readonly specifier: string;
|
|
20
|
+
}
|
|
21
|
+
/** The relative source of a staged asset. The page directory is self-contained, thus the source is `assets/<file>`. */
|
|
22
|
+
export declare function assetSource(asset: PageAsset): string;
|
|
23
|
+
/** The chart runtime. The page-side bootstrap reads the `echarts` global that this file declares. */
|
|
24
|
+
export declare const ECHARTS_ASSET: PageAsset;
|
|
25
|
+
/** The sans font. The file is the latin subset of the variable font, thus one file carries each weight. */
|
|
26
|
+
export declare const SANS_FONT_ASSET: PageAsset;
|
|
27
|
+
/**
|
|
28
|
+
* The mono font at weight 400, for the body of a data cell.
|
|
29
|
+
*
|
|
30
|
+
* Fontsource publishes no variable IBM Plex Mono, thus each weight is a separate file and a separate entry.
|
|
31
|
+
* The identity uses four weights: 400 for a data cell, 500 for a tag, 600 for a table header and a badge,
|
|
32
|
+
* and 700 for a metric value.
|
|
33
|
+
*/
|
|
34
|
+
export declare const MONO_FONT_400_ASSET: PageAsset;
|
|
35
|
+
/** The mono font at weight 500, for a tag and a stat-card label. */
|
|
36
|
+
export declare const MONO_FONT_500_ASSET: PageAsset;
|
|
37
|
+
/** The mono font at weight 600, for a table header, a section label, and the panel badge. */
|
|
38
|
+
export declare const MONO_FONT_600_ASSET: PageAsset;
|
|
39
|
+
/** The mono font at weight 700, for a metric value. */
|
|
40
|
+
export declare const MONO_FONT_700_ASSET: PageAsset;
|
|
41
|
+
/** Each asset that the caller stages beside the page. */
|
|
42
|
+
export declare const PAGE_ASSETS: readonly PageAsset[];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The asset manifest of the page.
|
|
3
|
+
*
|
|
4
|
+
* The page loads its chart runtime and its fonts from the sibling `assets/` directory. Thus each entry
|
|
5
|
+
* names the staged file and the module specifier of its source. The caller resolves each specifier against
|
|
6
|
+
* its own installation, and it copies the bytes beside the page.
|
|
7
|
+
*
|
|
8
|
+
* The module holds data alone. The renderer reads no file, thus the page stays a pure function of its
|
|
9
|
+
* inputs.
|
|
10
|
+
*/
|
|
11
|
+
/** The directory of the staged assets, relative to the page. */
|
|
12
|
+
export const ASSETS_DIR = "assets";
|
|
13
|
+
/** The relative source of a staged asset. The page directory is self-contained, thus the source is `assets/<file>`. */
|
|
14
|
+
export function assetSource(asset) {
|
|
15
|
+
return `${ASSETS_DIR}/${asset.file}`;
|
|
16
|
+
}
|
|
17
|
+
/** The chart runtime. The page-side bootstrap reads the `echarts` global that this file declares. */
|
|
18
|
+
export const ECHARTS_ASSET = {
|
|
19
|
+
file: "echarts.min.js",
|
|
20
|
+
specifier: "echarts/dist/echarts.min.js",
|
|
21
|
+
};
|
|
22
|
+
/** The sans font. The file is the latin subset of the variable font, thus one file carries each weight. */
|
|
23
|
+
export const SANS_FONT_ASSET = {
|
|
24
|
+
file: "space-grotesk-latin-wght-normal.woff2",
|
|
25
|
+
specifier: "@fontsource-variable/space-grotesk/files/space-grotesk-latin-wght-normal.woff2",
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The mono font at weight 400, for the body of a data cell.
|
|
29
|
+
*
|
|
30
|
+
* Fontsource publishes no variable IBM Plex Mono, thus each weight is a separate file and a separate entry.
|
|
31
|
+
* The identity uses four weights: 400 for a data cell, 500 for a tag, 600 for a table header and a badge,
|
|
32
|
+
* and 700 for a metric value.
|
|
33
|
+
*/
|
|
34
|
+
export const MONO_FONT_400_ASSET = {
|
|
35
|
+
file: "ibm-plex-mono-latin-400-normal.woff2",
|
|
36
|
+
specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-400-normal.woff2",
|
|
37
|
+
};
|
|
38
|
+
/** The mono font at weight 500, for a tag and a stat-card label. */
|
|
39
|
+
export const MONO_FONT_500_ASSET = {
|
|
40
|
+
file: "ibm-plex-mono-latin-500-normal.woff2",
|
|
41
|
+
specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2",
|
|
42
|
+
};
|
|
43
|
+
/** The mono font at weight 600, for a table header, a section label, and the panel badge. */
|
|
44
|
+
export const MONO_FONT_600_ASSET = {
|
|
45
|
+
file: "ibm-plex-mono-latin-600-normal.woff2",
|
|
46
|
+
specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-600-normal.woff2",
|
|
47
|
+
};
|
|
48
|
+
/** The mono font at weight 700, for a metric value. */
|
|
49
|
+
export const MONO_FONT_700_ASSET = {
|
|
50
|
+
file: "ibm-plex-mono-latin-700-normal.woff2",
|
|
51
|
+
specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-700-normal.woff2",
|
|
52
|
+
};
|
|
53
|
+
/** Each asset that the caller stages beside the page. */
|
|
54
|
+
export const PAGE_ASSETS = [
|
|
55
|
+
ECHARTS_ASSET,
|
|
56
|
+
SANS_FONT_ASSET,
|
|
57
|
+
MONO_FONT_400_ASSET,
|
|
58
|
+
MONO_FONT_500_ASSET,
|
|
59
|
+
MONO_FONT_600_ASSET,
|
|
60
|
+
MONO_FONT_700_ASSET,
|
|
61
|
+
];
|
|
@@ -20,6 +20,23 @@ import { normalizeEchartSpec } from "../tools/display/normalize-echart-spec.js";
|
|
|
20
20
|
* scale maps the low value to `#440154` and the high value to `#fde725`.
|
|
21
21
|
*/
|
|
22
22
|
const VIRIDIS = ["#440154", "#482777", "#3e4989", "#31688e", "#26828e", "#1f9e89", "#35b779", "#6ece58", "#b5de2b", "#fde725"];
|
|
23
|
+
/**
|
|
24
|
+
* The distance in pixels between the x axis line and its name.
|
|
25
|
+
*
|
|
26
|
+
* The name sits under the middle of the axis, thus the gap must clear the axis labels below the line. The
|
|
27
|
+
* value is a fixed constant, thus the derivation stays deterministic.
|
|
28
|
+
*/
|
|
29
|
+
const X_AXIS_NAME_GAP = 34;
|
|
30
|
+
/**
|
|
31
|
+
* The name fields of an x axis.
|
|
32
|
+
*
|
|
33
|
+
* `normalizeEchartSpec` holds the grid to a right margin of 5 percent. The ECharts default `nameLocation`
|
|
34
|
+
* of `"end"` puts the name at the right end of the axis, thus the name runs past that margin and the panel
|
|
35
|
+
* clips it. A centered name sits under the middle of the axis, and no margin can cut it.
|
|
36
|
+
*/
|
|
37
|
+
function xAxisName(column) {
|
|
38
|
+
return { name: column, nameLocation: "middle", nameGap: X_AXIS_NAME_GAP };
|
|
39
|
+
}
|
|
23
40
|
/**
|
|
24
41
|
* Derive the ECharts option for a chart block, then pass it through `normalizeEchartSpec`.
|
|
25
42
|
*
|
|
@@ -68,7 +85,7 @@ function deriveBar(block, rows, columns) {
|
|
|
68
85
|
data: groupRows.map((row) => [row[x], row[y]]),
|
|
69
86
|
}));
|
|
70
87
|
return ok({
|
|
71
|
-
xAxis: { type: "category", data: categories,
|
|
88
|
+
xAxis: { type: "category", data: categories, ...xAxisName(x) },
|
|
72
89
|
yAxis: { type: "value", name: y },
|
|
73
90
|
series,
|
|
74
91
|
});
|
|
@@ -90,8 +107,8 @@ function deriveLine(block, rows, columns) {
|
|
|
90
107
|
data: sortByX(groupRows.map((row) => [row[x], row[y]])),
|
|
91
108
|
}));
|
|
92
109
|
return ok({
|
|
93
|
-
xAxis: inferAxis(rows, x),
|
|
94
|
-
yAxis: inferAxis(rows, y),
|
|
110
|
+
xAxis: inferAxis(rows, x, "x"),
|
|
111
|
+
yAxis: inferAxis(rows, y, "y"),
|
|
95
112
|
series,
|
|
96
113
|
});
|
|
97
114
|
}
|
|
@@ -113,8 +130,8 @@ function deriveScatter(block, rows, columns) {
|
|
|
113
130
|
data: groupRows.map((row) => [row[x], row[y]]),
|
|
114
131
|
}));
|
|
115
132
|
return ok({
|
|
116
|
-
xAxis: inferAxis(rows, x),
|
|
117
|
-
yAxis: inferAxis(rows, y),
|
|
133
|
+
xAxis: inferAxis(rows, x, "x"),
|
|
134
|
+
yAxis: inferAxis(rows, y, "y"),
|
|
118
135
|
series,
|
|
119
136
|
});
|
|
120
137
|
}
|
|
@@ -199,7 +216,7 @@ function deriveBox(block, rows, columns) {
|
|
|
199
216
|
}
|
|
200
217
|
}
|
|
201
218
|
return ok({
|
|
202
|
-
xAxis: { type: "category", data: categories,
|
|
219
|
+
xAxis: { type: "category", data: categories, ...xAxisName(x) },
|
|
203
220
|
yAxis: { type: "value", name: y },
|
|
204
221
|
series,
|
|
205
222
|
});
|
|
@@ -242,7 +259,7 @@ function deriveHeatmap(block, rows, columns) {
|
|
|
242
259
|
const min = finite.length > 0 ? Math.min(...finite) : 0;
|
|
243
260
|
const max = finite.length > 0 ? Math.max(...finite) : 1;
|
|
244
261
|
return ok({
|
|
245
|
-
xAxis: { type: "category", data: xCategories.map(String),
|
|
262
|
+
xAxis: { type: "category", data: xCategories.map(String), ...xAxisName(x), splitArea: { show: true } },
|
|
246
263
|
yAxis: { type: "category", data: yCategories.map(String), name: y, splitArea: { show: true } },
|
|
247
264
|
visualMap: {
|
|
248
265
|
type: "continuous",
|
|
@@ -355,12 +372,16 @@ function numericColumn(rows, column) {
|
|
|
355
372
|
/**
|
|
356
373
|
* The axis for a line or a scatter column. A column with any non-numeric string cell is a category axis
|
|
357
374
|
* with its distinct values as strings. Any other column is a value axis with `scale: true`.
|
|
375
|
+
*
|
|
376
|
+
* The `axis` argument names the channel that the column feeds. An x column takes the centered name, and a
|
|
377
|
+
* y column keeps the default name placement.
|
|
358
378
|
*/
|
|
359
|
-
function inferAxis(rows, column) {
|
|
379
|
+
function inferAxis(rows, column, axis) {
|
|
380
|
+
const nameFields = axis === "x" ? xAxisName(column) : { name: column };
|
|
360
381
|
if (rows.some((row) => isNonNumericString(row[column]))) {
|
|
361
|
-
return { type: "category", data: firstAppearance(rows.map((row) => row[column])).map(String),
|
|
382
|
+
return { type: "category", data: firstAppearance(rows.map((row) => row[column])).map(String), ...nameFields };
|
|
362
383
|
}
|
|
363
|
-
return { type: "value", scale: true,
|
|
384
|
+
return { type: "value", scale: true, ...nameFields };
|
|
364
385
|
}
|
|
365
386
|
/**
|
|
366
387
|
* Build one series per group, in first-appearance order of the group values. A chart with no group
|