@inflexa-ai/harness 0.48.0 → 0.48.1
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/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/runtime/dbos.d.ts +6 -0
- package/dist/runtime/dbos.js +14 -2
- package/dist/state/artifacts.d.ts +4 -1
- package/dist/state/artifacts.js +4 -1
- package/dist/state/index.d.ts +2 -2
- package/dist/state/index.js +1 -1
- package/dist/state/plans.d.ts +27 -0
- package/dist/state/plans.js +57 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -190,8 +190,8 @@ export { planGraphOf } from "./workflows/plan/graph.js";
|
|
|
190
190
|
export { planPartOf } from "./memory/card-builders.js";
|
|
191
191
|
export { validatePlan } from "./schemas/validate-plan.js";
|
|
192
192
|
export type { ValidationResult } from "./schemas/validate-plan.js";
|
|
193
|
-
export { upsertPlan, loadPlan, loadPlanRecord } from "./state/plans.js";
|
|
194
|
-
export type { UpsertPlanInput, PlanRecord } from "./state/plans.js";
|
|
193
|
+
export { upsertPlan, loadPlan, loadPlanRecord, queryPlanPage } from "./state/plans.js";
|
|
194
|
+
export type { UpsertPlanInput, PlanRecord, PlanPage, PlanPageEntry } from "./state/plans.js";
|
|
195
195
|
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, countRunsByAnalysis, queryRunIdsByPlan, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
196
196
|
export type { InsertRunInput, RunReservation } from "./state/runs.js";
|
|
197
197
|
export { queryStepsByRun, countStepsByRuns } from "./state/step-executions.js";
|
package/dist/index.js
CHANGED
|
@@ -293,7 +293,7 @@ export { validatePlan } from "./schemas/validate-plan.js";
|
|
|
293
293
|
// Plan + run + step ledgers around a trigger. `upsertPlan` takes a
|
|
294
294
|
// caller-derived id and inserts-if-absent (the deterministic-id intake path);
|
|
295
295
|
// the run-state calls mirror the chat trigger's reserve → dedup → status flow.
|
|
296
|
-
export { upsertPlan, loadPlan, loadPlanRecord } from "./state/plans.js";
|
|
296
|
+
export { upsertPlan, loadPlan, loadPlanRecord, queryPlanPage } from "./state/plans.js";
|
|
297
297
|
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, countRunsByAnalysis, queryRunIdsByPlan, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
298
298
|
// The step ledger reads of a run, and the step counts of each run of a list in one statement.
|
|
299
299
|
export { queryStepsByRun, countStepsByRuns } from "./state/step-executions.js";
|
package/dist/runtime/dbos.d.ts
CHANGED
|
@@ -98,6 +98,12 @@ export type WorkflowSchedule = {
|
|
|
98
98
|
* schedule of this list. A second call with the same name replaces the first.
|
|
99
99
|
*/
|
|
100
100
|
export declare function scheduleWorkflow(schedule: WorkflowSchedule): void;
|
|
101
|
+
/**
|
|
102
|
+
* Store each schedule of {@link scheduleWorkflow} in the system database, and delete each stored schedule of this
|
|
103
|
+
* application that no code registers. DBOS only upserts, thus a schedule of a removed workflow fires on each tick and
|
|
104
|
+
* finds no function. Each schedule of the harness goes through `scheduleWorkflow`.
|
|
105
|
+
*/
|
|
106
|
+
export declare function applyWorkflowSchedules(): Promise<void>;
|
|
101
107
|
/**
|
|
102
108
|
* Launch DBOS. Idempotent — a second call is a no-op so tests that drive
|
|
103
109
|
* the harness twice (or accidentally double-import) don't re-launch.
|
package/dist/runtime/dbos.js
CHANGED
|
@@ -130,6 +130,19 @@ const pendingSchedules = new Map();
|
|
|
130
130
|
export function scheduleWorkflow(schedule) {
|
|
131
131
|
pendingSchedules.set(schedule.scheduleName, schedule);
|
|
132
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Store each schedule of {@link scheduleWorkflow} in the system database, and delete each stored schedule of this
|
|
135
|
+
* application that no code registers. DBOS only upserts, thus a schedule of a removed workflow fires on each tick and
|
|
136
|
+
* finds no function. Each schedule of the harness goes through `scheduleWorkflow`.
|
|
137
|
+
*/
|
|
138
|
+
export async function applyWorkflowSchedules() {
|
|
139
|
+
if (pendingSchedules.size > 0)
|
|
140
|
+
await DBOS.applySchedules([...pendingSchedules.values()]);
|
|
141
|
+
for (const stored of await DBOS.listSchedules()) {
|
|
142
|
+
if (!pendingSchedules.has(stored.scheduleName))
|
|
143
|
+
await DBOS.deleteSchedule(stored.scheduleName);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
133
146
|
let resultRecipesRegistered = false;
|
|
134
147
|
/**
|
|
135
148
|
* DBOS refuses a registration after the launch, and the registry of SuperJSON
|
|
@@ -160,8 +173,7 @@ export async function launchDbos({ config, logger: injected }) {
|
|
|
160
173
|
registerResultSerialization();
|
|
161
174
|
const start = performance.now();
|
|
162
175
|
await DBOS.launch();
|
|
163
|
-
|
|
164
|
-
await DBOS.applySchedules([...pendingSchedules.values()]);
|
|
176
|
+
await applyWorkflowSchedules();
|
|
165
177
|
state.launched = true;
|
|
166
178
|
state.recoveryStarted = true;
|
|
167
179
|
logger.info("launched", {
|
|
@@ -86,6 +86,8 @@ export type ArtifactPageQuery = {
|
|
|
86
86
|
q?: string;
|
|
87
87
|
/** The whole path, with case. It selects the one row of that path. */
|
|
88
88
|
path?: string;
|
|
89
|
+
/** The role of the row. `step_output` selects the rows that the grouped run count of {@link countArtifactsByRuns} counts. */
|
|
90
|
+
role?: ArtifactRole;
|
|
89
91
|
/**
|
|
90
92
|
* Extensions in lower case, with no dot. A row is selected when the extension of its path is a member. A row with
|
|
91
93
|
* no extension never matches, and an empty list selects each row. The read compares the members as they are.
|
|
@@ -125,7 +127,8 @@ export type ArtifactPage = {
|
|
|
125
127
|
* select, grouped by extension, and the page. The count is a separate statement because a page past the end has no
|
|
126
128
|
* row to carry it. `total` is the sum of the groups.
|
|
127
129
|
*
|
|
128
|
-
* Every row of the analysis is a member, an `input` row and a row with `unrecoverable_at` included
|
|
130
|
+
* Every row of the analysis is a member, an `input` row and a row with `unrecoverable_at` included, unless the `role`
|
|
131
|
+
* filter selects one role. `total` and `extensionCounts` follow the filters. The cost
|
|
129
132
|
* grows with the ledger of the one analysis, because the count reads each filtered row.
|
|
130
133
|
*/
|
|
131
134
|
export declare function queryArtifactPage(pool: Querier, analysisId: string, query: ArtifactPageQuery): ResultAsync<ArtifactPage, DbError>;
|
package/dist/state/artifacts.js
CHANGED
|
@@ -193,7 +193,8 @@ const EXTENSION_SQL = `lower(substring(regexp_replace(path, '^.*/', '') from '^.
|
|
|
193
193
|
* select, grouped by extension, and the page. The count is a separate statement because a page past the end has no
|
|
194
194
|
* row to carry it. `total` is the sum of the groups.
|
|
195
195
|
*
|
|
196
|
-
* Every row of the analysis is a member, an `input` row and a row with `unrecoverable_at` included
|
|
196
|
+
* Every row of the analysis is a member, an `input` row and a row with `unrecoverable_at` included, unless the `role`
|
|
197
|
+
* filter selects one role. `total` and `extensionCounts` follow the filters. The cost
|
|
197
198
|
* grows with the ledger of the one analysis, because the count reads each filtered row.
|
|
198
199
|
*/
|
|
199
200
|
export function queryArtifactPage(pool, analysisId, query) {
|
|
@@ -215,6 +216,8 @@ export function queryArtifactPage(pool, analysisId, query) {
|
|
|
215
216
|
conditions.push(`strpos(lower(path), lower(${bind(query.q)}::text)) > 0`);
|
|
216
217
|
if (query.path !== undefined)
|
|
217
218
|
conditions.push(`path = ${bind(query.path)}`);
|
|
219
|
+
if (query.role !== undefined)
|
|
220
|
+
conditions.push(`role = ${bind(query.role)}`);
|
|
218
221
|
if (query.extensions !== undefined && query.extensions.length > 0) {
|
|
219
222
|
conditions.push(`${EXTENSION_SQL} = ANY(${bind([...query.extensions])}::text[])`);
|
|
220
223
|
}
|
package/dist/state/index.d.ts
CHANGED
|
@@ -16,8 +16,8 @@ setRunSynthesisOutcome, queryRun, queryActiveRun, queryNonTerminalRunsByAnalysis
|
|
|
16
16
|
export type { InsertRunInput, RunPage, RunReservation } from "./runs.js";
|
|
17
17
|
export { insertStepExecution, seedStepExecutions, sweepPendingStepExecutions, updateStepExecution, queryStepsByRun, countStepsByRuns, } from "./step-executions.js";
|
|
18
18
|
export type { InsertStepExecutionInput, RunStepCounts, SeedStepExecutionRow, UpdateStepExecutionInput } from "./step-executions.js";
|
|
19
|
-
export { insertPlan, upsertPlan, loadPlan, loadPlanRecord } from "./plans.js";
|
|
20
|
-
export type { InsertPlanInput, UpsertPlanInput, PlanRecord, PlanRowQuerier } from "./plans.js";
|
|
19
|
+
export { insertPlan, upsertPlan, loadPlan, loadPlanRecord, queryPlanPage } from "./plans.js";
|
|
20
|
+
export type { InsertPlanInput, UpsertPlanInput, PlanRecord, PlanRowQuerier, PlanPage, PlanPageEntry } from "./plans.js";
|
|
21
21
|
export { tryStartDataProfile, tryRetryDataProfile, tryRerunDataProfile, completeDataProfile, failDataProfile, expireStaleDataProfile, reconcileOrphanedDataProfile, clearDataProfile, completeEmptyDataProfile, loadDataProfileStatus, loadSeedInputFileIds, recordDataProfileWorkflowId, } from "./data-profile.js";
|
|
22
22
|
export type { DataProfileStatus, DataProfileTerminalWrite, DataProfileResult, DataProfileChecked, DataProfileCompanionCompleteness, DataProfileDimension, DataProfileGroup, DataProfileGroupSlot, DataProfileFile, DataProfileMemberAnnotation, DataProfileObservation, DataProfilePartition, DataProfileProbeReport, DataProfileQualityAssessment, DataProfileQuarantine, DataProfileRecipeStep, DataProfileKind, DataProfileAxis, DataProfileInputSignature, DataProfileCoverage, } from "./data-profile.js";
|
|
23
23
|
export { queryRunCountsByAnalyses, queryThreadCountsByAnalyses, queryDataProfileStatusByAnalyses, queryLatestRunsByAnalyses, queryRunProgressByRuns, queryRunsEndedSince, } from "./analyses-metrics.js";
|
package/dist/state/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { upsertArtifact, upsertArtifacts, queryInputArtifacts, queryStepArtifact
|
|
|
12
12
|
export { insertRun, reserveRunById, RunDedupCollisionError, RunIdentityCollisionError, updateRunStatus, reconcileOrphanedRuns, ORPHANED_RUN_GRACE_MS, markRunCanceledIfActive, promoteFailedToPartial, setRunMandate, // oss-core-managed-ok: run-mandate ledger (nullable; OSS leaves null)
|
|
13
13
|
setRunSynthesisOutcome, queryRun, queryActiveRun, queryNonTerminalRunsByAnalysis, queryRunsForInspection, queryRunsByAnalysis, countRunsByAnalysis, queryRunsByThread, queryRunIdsByPlan, queryNewestPlanRun, } from "./runs.js";
|
|
14
14
|
export { insertStepExecution, seedStepExecutions, sweepPendingStepExecutions, updateStepExecution, queryStepsByRun, countStepsByRuns, } from "./step-executions.js";
|
|
15
|
-
export { insertPlan, upsertPlan, loadPlan, loadPlanRecord } from "./plans.js";
|
|
15
|
+
export { insertPlan, upsertPlan, loadPlan, loadPlanRecord, queryPlanPage } from "./plans.js";
|
|
16
16
|
export { tryStartDataProfile, tryRetryDataProfile, tryRerunDataProfile, completeDataProfile, failDataProfile, expireStaleDataProfile, reconcileOrphanedDataProfile, clearDataProfile, completeEmptyDataProfile, loadDataProfileStatus, loadSeedInputFileIds, recordDataProfileWorkflowId, } from "./data-profile.js";
|
|
17
17
|
export { queryRunCountsByAnalyses, queryThreadCountsByAnalyses, queryDataProfileStatusByAnalyses, queryLatestRunsByAnalyses, queryRunProgressByRuns, queryRunsEndedSince, } from "./analyses-metrics.js";
|
|
18
18
|
// Not per-entity: one analysis's whole Postgres footprint, reached from its id
|
package/dist/state/plans.d.ts
CHANGED
|
@@ -94,3 +94,30 @@ export type PlanRecord = {
|
|
|
94
94
|
export declare function loadPlanRecord(pool: PlanRowQuerier, planId: string, opts: {
|
|
95
95
|
analysisId: string;
|
|
96
96
|
}): ResultAsync<PlanRecord | null, DbError>;
|
|
97
|
+
/** One plan of {@link queryPlanPage}. */
|
|
98
|
+
export type PlanPageEntry = {
|
|
99
|
+
planId: string;
|
|
100
|
+
createdAt: string;
|
|
101
|
+
/** The `title` of the stored document when it is a string, as the document holds it, else `null`. */
|
|
102
|
+
title: string | null;
|
|
103
|
+
parentPlanId: string | null;
|
|
104
|
+
/** The newest plan of the same analysis that names this plan as its parent. */
|
|
105
|
+
supersededBy: string | null;
|
|
106
|
+
/** The newest run of the plan, in the order of {@link queryRunIdsByPlan}, or `null` for a plan with no run. */
|
|
107
|
+
newestRunId: string | null;
|
|
108
|
+
};
|
|
109
|
+
/** One page of the plans of an analysis, and the exact count of them. */
|
|
110
|
+
export type PlanPage = {
|
|
111
|
+
entries: PlanPageEntry[];
|
|
112
|
+
total: number;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* One page of the plans of an analysis, newest first, in two statements: the count of the plans and the page. The
|
|
116
|
+
* count is a separate statement because a page past the end has no row to carry it. The order is the one of
|
|
117
|
+
* `supersededBy`: the creation time, then the highest plan id. The page gives the title of the document, not the
|
|
118
|
+
* document, because a page holds up to 200 plans.
|
|
119
|
+
*/
|
|
120
|
+
export declare function queryPlanPage(pool: Querier, analysisId: string, opts: {
|
|
121
|
+
limit: number;
|
|
122
|
+
offset: number;
|
|
123
|
+
}): ResultAsync<PlanPage, DbError>;
|
package/dist/state/plans.js
CHANGED
|
@@ -117,6 +117,13 @@ const PlanRecordRowSchema = z.object({
|
|
|
117
117
|
created_at: z.string(),
|
|
118
118
|
superseded_by: z.string().nullable(),
|
|
119
119
|
});
|
|
120
|
+
/**
|
|
121
|
+
* The `superseded_by` column of a plan read: the newest plan of the same analysis that names the plan as its parent.
|
|
122
|
+
* The text refers to the plan row of the outer query as `p`.
|
|
123
|
+
*/
|
|
124
|
+
const SUPERSEDED_BY_SQL = `(SELECT c.plan_id FROM cortex_plans c
|
|
125
|
+
WHERE c.parent_plan_id = p.plan_id AND c.analysis_id = p.analysis_id
|
|
126
|
+
ORDER BY c.created_at DESC, c.plan_id DESC LIMIT 1) AS superseded_by`;
|
|
120
127
|
/**
|
|
121
128
|
* Analysis-scoped read of one plan with its parent and the newest plan that
|
|
122
129
|
* replaced it, in one statement. Returns `ok(null)` for an unknown planId OR a
|
|
@@ -129,9 +136,7 @@ export function loadPlanRecord(pool, planId, opts) {
|
|
|
129
136
|
return tryQuery("plans.loadPlanRecord", async () => {
|
|
130
137
|
const result = await pool.query({
|
|
131
138
|
text: `SELECT p.plan, p.parent_plan_id, p.created_at,
|
|
132
|
-
|
|
133
|
-
WHERE c.parent_plan_id = p.plan_id AND c.analysis_id = p.analysis_id
|
|
134
|
-
ORDER BY c.created_at DESC, c.plan_id DESC LIMIT 1) AS superseded_by
|
|
139
|
+
${SUPERSEDED_BY_SQL}
|
|
135
140
|
FROM cortex_plans p
|
|
136
141
|
WHERE p.plan_id = $1 AND p.analysis_id = $2`,
|
|
137
142
|
values: [planId, opts.analysisId],
|
|
@@ -143,3 +148,52 @@ export function loadPlanRecord(pool, planId, opts) {
|
|
|
143
148
|
return { planId, plan: row.plan, parentPlanId: row.parent_plan_id, supersededBy: row.superseded_by, createdAt: row.created_at };
|
|
144
149
|
});
|
|
145
150
|
}
|
|
151
|
+
const PlanPageRowSchema = z.object({
|
|
152
|
+
plan_id: z.string(),
|
|
153
|
+
created_at: z.string(),
|
|
154
|
+
title: z.string().nullable(),
|
|
155
|
+
parent_plan_id: z.string().nullable(),
|
|
156
|
+
superseded_by: z.string().nullable(),
|
|
157
|
+
newest_run_id: z.string().nullable(),
|
|
158
|
+
});
|
|
159
|
+
/**
|
|
160
|
+
* One page of the plans of an analysis, newest first, in two statements: the count of the plans and the page. The
|
|
161
|
+
* count is a separate statement because a page past the end has no row to carry it. The order is the one of
|
|
162
|
+
* `supersededBy`: the creation time, then the highest plan id. The page gives the title of the document, not the
|
|
163
|
+
* document, because a page holds up to 200 plans.
|
|
164
|
+
*/
|
|
165
|
+
export function queryPlanPage(pool, analysisId, opts) {
|
|
166
|
+
return tryQuery("plans.queryPlanPage", async () => {
|
|
167
|
+
const countResult = await pool.query({
|
|
168
|
+
text: "SELECT COUNT(*)::int AS total FROM cortex_plans WHERE analysis_id = $1",
|
|
169
|
+
values: [analysisId],
|
|
170
|
+
});
|
|
171
|
+
const rowsResult = await pool.query({
|
|
172
|
+
text: `SELECT p.plan_id, p.created_at, p.parent_plan_id,
|
|
173
|
+
CASE WHEN jsonb_typeof(p.plan->'title') = 'string' THEN p.plan->>'title' END AS title,
|
|
174
|
+
${SUPERSEDED_BY_SQL},
|
|
175
|
+
(SELECT r.run_id FROM cortex_runs r
|
|
176
|
+
WHERE r.analysis_id = p.analysis_id AND r.plan_id = p.plan_id
|
|
177
|
+
ORDER BY r.started_at DESC, r.run_id DESC LIMIT 1) AS newest_run_id
|
|
178
|
+
FROM cortex_plans p
|
|
179
|
+
WHERE p.analysis_id = $1
|
|
180
|
+
ORDER BY p.created_at DESC, p.plan_id DESC
|
|
181
|
+
LIMIT $2 OFFSET $3`,
|
|
182
|
+
values: [analysisId, opts.limit, opts.offset],
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
entries: rowsResult.rows.map((raw) => {
|
|
186
|
+
const row = PlanPageRowSchema.parse(raw);
|
|
187
|
+
return {
|
|
188
|
+
planId: row.plan_id,
|
|
189
|
+
createdAt: row.created_at,
|
|
190
|
+
title: row.title,
|
|
191
|
+
parentPlanId: row.parent_plan_id,
|
|
192
|
+
supersededBy: row.superseded_by,
|
|
193
|
+
newestRunId: row.newest_run_id,
|
|
194
|
+
};
|
|
195
|
+
}),
|
|
196
|
+
total: countResult.rows[0]?.total ?? 0,
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
package/package.json
CHANGED