@inflexa-ai/harness 0.44.0 → 0.45.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/app/approve-plan.d.ts +48 -0
- package/dist/app/approve-plan.js +84 -0
- package/dist/app/data-profile-view.d.ts +1 -1
- package/dist/app/run-activity.js +4 -6
- package/dist/contracts/chat-events.d.ts +2 -0
- package/dist/contracts/chat-frame.d.ts +7 -0
- package/dist/contracts/chat-frame.js +18 -4
- package/dist/contracts/chat-parts.d.ts +73 -2
- package/dist/contracts/chat-parts.js +3 -1
- package/dist/contracts/index.d.ts +3 -1
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/part-registry.js +3 -0
- package/dist/contracts/schemas/chat-events.d.ts +13 -0
- package/dist/contracts/schemas/chat-events.js +1 -0
- package/dist/contracts/schemas/chat-parts.d.ts +200 -15
- package/dist/contracts/schemas/chat-parts.js +63 -12
- package/dist/contracts/schemas/index.d.ts +1 -1
- package/dist/contracts/schemas/index.js +1 -1
- package/dist/contracts/subagent-activity.d.ts +12 -0
- package/dist/contracts/subagent-activity.js +76 -0
- package/dist/execution/artifact-metadata.js +1 -3
- package/dist/execution/run-canceler.js +1 -1
- package/dist/execution/run-event-parts.d.ts +6 -5
- package/dist/execution/run-event-parts.js +6 -5
- package/dist/execution/run-event-stream.d.ts +18 -6
- package/dist/execution/run-event-stream.js +67 -17
- package/dist/execution/run-synthesis.d.ts +21 -0
- package/dist/execution/run-synthesis.js +77 -3
- package/dist/index.d.ts +22 -8
- package/dist/index.js +16 -2
- package/dist/lib/hooks.d.ts +1 -1
- package/dist/lib/metrics.d.ts +2 -2
- package/dist/loop/run-agent.js +1 -0
- package/dist/loop/types.d.ts +2 -0
- package/dist/memory/card-builders.d.ts +11 -0
- package/dist/memory/card-builders.js +35 -21
- package/dist/memory/conversation-display-recorder.d.ts +2 -0
- package/dist/memory/conversation-display-recorder.js +10 -1
- package/dist/memory/conversation-display-storage.d.ts +9 -2
- package/dist/memory/conversation-display-storage.js +18 -3
- package/dist/memory/thread-history.d.ts +8 -1
- package/dist/memory/thread-history.js +13 -3
- package/dist/memory/thread-store.d.ts +13 -0
- package/dist/memory/thread-store.js +15 -1
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +3 -1
- package/dist/provenance/seam.d.ts +16 -5
- package/dist/providers/ai-sdk.js +6 -4
- package/dist/report-model/pin-snapshot.d.ts +1 -1
- package/dist/report-model/pin-snapshot.js +2 -50
- package/dist/runtime/assemble.d.ts +8 -1
- package/dist/runtime/assemble.js +11 -0
- package/dist/runtime/boot.d.ts +3 -0
- package/dist/runtime/boot.js +10 -1
- package/dist/sandbox/sandbox-step-translate.d.ts +23 -0
- package/dist/sandbox/sandbox-step-translate.js +24 -0
- package/dist/schemas/artifact-manifest.d.ts +1 -1
- package/dist/schemas/artifact-manifest.js +11 -9
- package/dist/schemas/plan-schemas.d.ts +18 -18
- package/dist/schemas/run-synthesis.d.ts +2 -2
- package/dist/state/analyses-metrics.d.ts +79 -7
- package/dist/state/analyses-metrics.js +141 -6
- package/dist/state/artifacts.d.ts +50 -3
- package/dist/state/artifacts.js +67 -7
- package/dist/state/data-profile.d.ts +4 -2
- package/dist/state/data-profile.js +7 -5
- package/dist/state/index.d.ts +9 -8
- package/dist/state/index.js +5 -5
- package/dist/state/migrations/20261012120000_index_the_runs_by_start.d.ts +8 -0
- package/dist/state/migrations/20261012120000_index_the_runs_by_start.js +12 -0
- package/dist/state/migrations/20261013120000_index_the_ended_runs_and_the_recent_threads.d.ts +8 -0
- package/dist/state/migrations/20261013120000_index_the_ended_runs_and_the_recent_threads.js +13 -0
- package/dist/state/migrations/20261013180000_narrow_the_active_run_index.d.ts +8 -0
- package/dist/state/migrations/20261013180000_narrow_the_active_run_index.js +13 -0
- package/dist/state/migrations/20261014120000_index_the_plans_by_parent_and_the_runs_by_plan.d.ts +8 -0
- package/dist/state/migrations/20261014120000_index_the_plans_by_parent_and_the_runs_by_plan.js +13 -0
- package/dist/state/migrations/index.js +8 -0
- package/dist/state/plans.d.ts +21 -0
- package/dist/state/plans.js +25 -0
- package/dist/state/runs.d.ts +36 -5
- package/dist/state/runs.js +65 -19
- package/dist/state/schema.d.ts +2 -0
- package/dist/tasks/data-profile-activity.d.ts +7 -1
- package/dist/tasks/data-profile-activity.js +18 -9
- package/dist/tasks/data-profile.d.ts +15 -1
- package/dist/tasks/data-profile.js +16 -11
- package/dist/tools/approval/gateway.js +28 -5
- package/dist/tools/display/show-user.d.ts +1 -1
- package/dist/tools/execute-analysis.d.ts +51 -0
- package/dist/tools/execute-analysis.js +241 -109
- package/dist/tools/research/inspect-data-profile.d.ts +1 -1
- package/dist/tools/research/inspect-run.d.ts +1 -1
- package/dist/tools/research/inspect-run.js +7 -7
- package/dist/workflows/execute-analysis.d.ts +4 -3
- package/dist/workflows/execute-analysis.js +35 -22
- package/dist/workflows/plan/graph.d.ts +16 -0
- package/dist/workflows/plan/graph.js +36 -0
- package/dist/workflows/sandbox-step.js +14 -12
- package/package.json +1 -1
|
@@ -296,7 +296,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
296
296
|
// ledger has no suspended state and the retry path takes a `failed` row again.
|
|
297
297
|
const suspendProfile = async (suspension) => {
|
|
298
298
|
logger.warn("profile suspended", { reason: suspension.reason });
|
|
299
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, suspension.reason)), "failed");
|
|
299
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, suspension.reason)), "failed", deps.notifyDataProfileSettled);
|
|
300
300
|
await revoke("data-profile-suspended");
|
|
301
301
|
unwrapOrThrow(await suspendAnalysis(deps.pool, analysisId));
|
|
302
302
|
await activity.failed(suspension.reason);
|
|
@@ -310,7 +310,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
310
310
|
// this capability, and a body that composed its own strings is a body they can drift from.
|
|
311
311
|
// The frame is the workflow's synthetic one: both values are constants shared by every analysis,
|
|
312
312
|
// so they identify nothing and consumers are required not to key on them.
|
|
313
|
-
const activity = createProfileActivityEmitter((part) => DBOS.writeStream("events", part), { runId: DATA_PROFILE_RUN_LITERAL, stepId: DATA_PROFILE_STEP_LITERAL }, logger);
|
|
313
|
+
const activity = createProfileActivityEmitter((part) => DBOS.writeStream("events", part), { runId: DATA_PROFILE_RUN_LITERAL, stepId: DATA_PROFILE_STEP_LITERAL }, logger, () => DBOS.now());
|
|
314
314
|
try {
|
|
315
315
|
// Record which workflow owns this profile before anything else, so a consumer that resolves
|
|
316
316
|
// the id from the ledger can subscribe from the earliest possible moment. Guarded on a real
|
|
@@ -337,7 +337,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
337
337
|
// catalogue's fit is measured, and a completion that emitted none would make the
|
|
338
338
|
// empty-manifest path invisible to the same monitoring that watches every other.
|
|
339
339
|
logProfileMonitoring(logger, { dimensions: [], probes: [], repairRounds: 0, absorb: "none" });
|
|
340
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId)), "completed");
|
|
340
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId)), "completed", deps.notifyDataProfileSettled);
|
|
341
341
|
await revoke("data-profile-completed");
|
|
342
342
|
// This is a terminal completion like any other, so it reports one — a consumer watching
|
|
343
343
|
// an empty-manifest profile sees it settle rather than seeing the stream simply stop.
|
|
@@ -390,7 +390,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
390
390
|
await activity.indexing();
|
|
391
391
|
await indexProfile({ deps, analysisId, session: runSession, record, scan, logger });
|
|
392
392
|
logProfileMonitoring(logger, { resolution, dimensions: [], probes: [], repairRounds: 0, absorb: "none" });
|
|
393
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, record)), "completed");
|
|
393
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, record)), "completed", deps.notifyDataProfileSettled);
|
|
394
394
|
await revoke("data-profile-completed");
|
|
395
395
|
await activity.complete();
|
|
396
396
|
return;
|
|
@@ -416,7 +416,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
416
416
|
repairRounds: 0,
|
|
417
417
|
absorb: absorb.kind,
|
|
418
418
|
});
|
|
419
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, absorbed)), "completed");
|
|
419
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, absorbed)), "completed", deps.notifyDataProfileSettled);
|
|
420
420
|
await revoke("data-profile-completed");
|
|
421
421
|
await activity.complete();
|
|
422
422
|
return;
|
|
@@ -571,7 +571,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
571
571
|
});
|
|
572
572
|
// 6. Complete — store the FULL profiler finding plus the input signature for
|
|
573
573
|
// staleness detection. The scratch tree is gone; this row is all that survives.
|
|
574
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, profileRecord)), "completed");
|
|
574
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, profileRecord)), "completed", deps.notifyDataProfileSettled);
|
|
575
575
|
await revoke("data-profile-completed");
|
|
576
576
|
// LAST statement of the success path, and that placement is the guarantee that exactly
|
|
577
577
|
// one terminal activity is ever emitted. Everything that could still throw — the ledger
|
|
@@ -598,7 +598,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
598
598
|
return await suspendProfile(suspension);
|
|
599
599
|
logger.error("profile failed", logger.errorFields(err));
|
|
600
600
|
const reason = profileFailureReason(err);
|
|
601
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, reason)), "failed");
|
|
601
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, reason)), "failed", deps.notifyDataProfileSettled);
|
|
602
602
|
await revoke("data-profile-failed");
|
|
603
603
|
// The same bounded, user-safe line the ledger receives — never the raw error, whose paths and
|
|
604
604
|
// stack frames stay in the log record above. This is the only other terminal emission, so
|
|
@@ -617,18 +617,23 @@ function logTerminalNoop(logger, analysisId, write) {
|
|
|
617
617
|
}
|
|
618
618
|
/**
|
|
619
619
|
* Settle one terminal write of the profile ledger. A refused stamp is logged
|
|
620
|
-
* (see `logTerminalNoop`). An accepted stamp records the
|
|
621
|
-
*
|
|
620
|
+
* (see `logTerminalNoop`) and sends no notice. An accepted stamp records the
|
|
621
|
+
* profile's run outcome and then delivers the settle notice when the deps bind
|
|
622
|
+
* one: the CAS admits one write per `running` claim, so this is the exactly-once
|
|
622
623
|
* point of the transition — a recovery replay of the body finds the row no
|
|
623
|
-
* longer `running`, and
|
|
624
|
+
* longer `running`, and does neither again. The duration runs from the
|
|
624
625
|
* `data_profile_started_at` the claim stamped, never from a body-local clock.
|
|
626
|
+
* A crash between the stamp and the notice loses that one notice.
|
|
625
627
|
*/
|
|
626
|
-
function settleTerminalWrite(logger, analysisId, write, status) {
|
|
628
|
+
async function settleTerminalWrite(logger, analysisId, write, status, notify) {
|
|
627
629
|
if (!write.stamped) {
|
|
628
630
|
logTerminalNoop(logger, analysisId, status === "completed" ? "completion" : "failure");
|
|
629
631
|
return;
|
|
630
632
|
}
|
|
631
633
|
recordRunCompleted({ workflow: "data_profile", status, durationMs: elapsedSinceIso(write.startedAt) });
|
|
634
|
+
if (notify === undefined)
|
|
635
|
+
return;
|
|
636
|
+
await deliverNotice(logger, "notifyDataProfileSettled", notify({ analysisId, status, completedAt: write.completedAt }));
|
|
632
637
|
}
|
|
633
638
|
/** A one-line, user-facing description of a `DataProfileStartError`. */
|
|
634
639
|
export function describeDataProfileStartError(e) {
|
|
@@ -40,8 +40,11 @@ export function createAskGateway(deps) {
|
|
|
40
40
|
// A standing grant this user holds for this ask's grant key — the command
|
|
41
41
|
// unless the tool supplied a broader key — short-circuits the prompt: record
|
|
42
42
|
// the invocation as `resolved` for the audit trail and return without pausing.
|
|
43
|
+
// The one part it emits is already settled, so no surface prompts, and the
|
|
44
|
+
// transcript still shows that a standing grant approved the call.
|
|
43
45
|
if (unwrapOrThrow(await selectGrant(pool, ctx.analysisId, ctx.userId, request.grantKey ?? request.command))) {
|
|
44
46
|
unwrapOrThrow(await insertGrantedAsk(pool, row, now));
|
|
47
|
+
await emitAskPart(ctx.emit, id, request, "resolved", "always");
|
|
45
48
|
return { kind: "always" };
|
|
46
49
|
}
|
|
47
50
|
unwrapOrThrow(await insertPendingAsk(pool, row));
|
|
@@ -69,7 +72,7 @@ export function createAskGateway(deps) {
|
|
|
69
72
|
throw new AskRejectedError("approval request is no longer available");
|
|
70
73
|
}
|
|
71
74
|
if (resolution.status !== "pending") {
|
|
72
|
-
await emitAskPart(ctx.emit, id, request, resolution.status);
|
|
75
|
+
await emitAskPart(ctx.emit, id, request, resolution.status, replyKindOf(resolution.status, resolution.reply));
|
|
73
76
|
return settle(resolution.status, resolution.reply, ctx.signal);
|
|
74
77
|
}
|
|
75
78
|
await sleepUntil(POLL_INTERVAL_MS, ctx.signal);
|
|
@@ -88,7 +91,7 @@ export function createAskGateway(deps) {
|
|
|
88
91
|
return { ask, answer, pending, sweepExpired };
|
|
89
92
|
}
|
|
90
93
|
/** Emit (or reconcile, under the same id) the ask's `data-ask` chat part. */
|
|
91
|
-
function emitAskPart(emit, id, request, status) {
|
|
94
|
+
function emitAskPart(emit, id, request, status, reply) {
|
|
92
95
|
return emit({
|
|
93
96
|
type: "data-ask",
|
|
94
97
|
data: {
|
|
@@ -97,16 +100,36 @@ function emitAskPart(emit, id, request, status) {
|
|
|
97
100
|
command: request.command,
|
|
98
101
|
...(request.detail !== undefined ? { detail: request.detail } : {}),
|
|
99
102
|
status,
|
|
103
|
+
...(reply !== undefined ? { reply } : {}),
|
|
100
104
|
},
|
|
101
105
|
});
|
|
102
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The kind of reply the terminal part shows: the kind that `settle` acts on for a `resolved` or `rejected` row,
|
|
109
|
+
* and none for any other status. It never reads the feedback of a rejection, which is for the model.
|
|
110
|
+
*/
|
|
111
|
+
function replyKindOf(status, reply) {
|
|
112
|
+
switch (status) {
|
|
113
|
+
case "resolved":
|
|
114
|
+
return approvalOf(reply).kind;
|
|
115
|
+
case "rejected":
|
|
116
|
+
return "reject";
|
|
117
|
+
case "expired":
|
|
118
|
+
case "aborted":
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** The approval that a `resolved` row grants. */
|
|
123
|
+
function approvalOf(reply) {
|
|
124
|
+
// Only `once` / `always` reach `resolved`; guard the JSONB round-trip and
|
|
125
|
+
// default to the narrowest approval if a malformed reply ever slipped in.
|
|
126
|
+
return reply && (reply.kind === "once" || reply.kind === "always") ? reply : { kind: "once" };
|
|
127
|
+
}
|
|
103
128
|
/** Resolve an approval or throw the terminal denial the ledger recorded. */
|
|
104
129
|
function settle(status, reply, signal) {
|
|
105
130
|
switch (status) {
|
|
106
131
|
case "resolved":
|
|
107
|
-
|
|
108
|
-
// default to the narrowest approval if a malformed reply ever slipped in.
|
|
109
|
-
return reply && (reply.kind === "once" || reply.kind === "always") ? reply : { kind: "once" };
|
|
132
|
+
return approvalOf(reply);
|
|
110
133
|
case "rejected":
|
|
111
134
|
throw new AskRejectedError(reply && reply.kind === "reject" ? reply.feedback : undefined);
|
|
112
135
|
case "expired":
|
|
@@ -26,7 +26,7 @@ type ShowUserOutput = {
|
|
|
26
26
|
id: string;
|
|
27
27
|
};
|
|
28
28
|
export declare const showUserTool: import("../define-tool.js").Tool<{
|
|
29
|
-
kind: "code" | "
|
|
29
|
+
kind: "code" | "echart" | "markdown" | "svg" | "table" | "structure";
|
|
30
30
|
title?: string | undefined;
|
|
31
31
|
spec?: Record<string, unknown> | undefined;
|
|
32
32
|
dataPath?: string | undefined;
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* Unified async edge for approved plans and explicitly requested one-step ad
|
|
3
3
|
* hoc analyses. Both modes launch the ordinary executeAnalysis workflow.
|
|
4
4
|
*/
|
|
5
|
+
import { ResultAsync } from "neverthrow";
|
|
5
6
|
import type { Pool } from "pg";
|
|
7
|
+
import type { AgentSession } from "../auth/types.js";
|
|
6
8
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
7
9
|
import type { EnvironmentStorePaths } from "../config/environment-stores.js";
|
|
8
10
|
import type { ResourcePolicy } from "../config/resource-limits.js";
|
|
@@ -54,7 +56,56 @@ export declare class PlanPackagesMissingError extends Error {
|
|
|
54
56
|
readonly unavailable?: string | undefined;
|
|
55
57
|
constructor(missing: string[], collisions: string[], unavailable?: string | undefined);
|
|
56
58
|
}
|
|
59
|
+
/** The deps that start the run of a stored plan. */
|
|
60
|
+
export type LaunchPlanRunDeps = Pick<ExecuteAnalysisToolDeps, "pool" | "executeAnalysisWorkflow" | "runAuthorizer" | "runLauncher" | "resourcePolicy" | "extendAnalysisFarm" | "logger">;
|
|
61
|
+
/** The run of a stored plan. `started` is false when a run of the plan was already active. */
|
|
62
|
+
export type PlanLaunch = {
|
|
63
|
+
runId: string;
|
|
64
|
+
started: boolean;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Why a stored plan did not start a run. `store_failed.cause` and `launch_failed.cause` are the
|
|
68
|
+
* values as the failing call gave them, thus a caller that must throw them keeps their class.
|
|
69
|
+
*/
|
|
70
|
+
export type LaunchPlanRunRefusal = {
|
|
71
|
+
type: "plan_not_found";
|
|
72
|
+
planId: string;
|
|
73
|
+
} | {
|
|
74
|
+
type: "plan_invalid";
|
|
75
|
+
planId: string;
|
|
76
|
+
malformed: boolean;
|
|
77
|
+
errors: string[];
|
|
78
|
+
} | {
|
|
79
|
+
type: "packages_missing";
|
|
80
|
+
missing: string[];
|
|
81
|
+
collisions: string[];
|
|
82
|
+
unavailable?: string;
|
|
83
|
+
} | {
|
|
84
|
+
type: "not_authorized";
|
|
85
|
+
reason: string;
|
|
86
|
+
} | {
|
|
87
|
+
type: "suspended";
|
|
88
|
+
reason: string;
|
|
89
|
+
} | {
|
|
90
|
+
type: "store_failed";
|
|
91
|
+
cause: unknown;
|
|
92
|
+
} | {
|
|
93
|
+
type: "launch_failed";
|
|
94
|
+
cause: unknown;
|
|
95
|
+
};
|
|
57
96
|
export declare function buildAdHocPlan(request: string, route: AdHocRoute, createdAt?: string): AnalysisPlan;
|
|
97
|
+
/**
|
|
98
|
+
* Start the run of a stored plan. It reads the plan under the analysis of the session, validates it,
|
|
99
|
+
* links its packages, reserves a run, authorizes it, and starts the workflow. It gives the run id, or
|
|
100
|
+
* the refusal, and it emits no chat part. `started` is false when a run of the plan was already active.
|
|
101
|
+
*
|
|
102
|
+
* This is an API boundary: a host calls it outside a tool body, thus no outcome above rejects the
|
|
103
|
+
* promise.
|
|
104
|
+
*/
|
|
105
|
+
export declare function launchPlanRun(deps: LaunchPlanRunDeps, input: {
|
|
106
|
+
session: AgentSession;
|
|
107
|
+
planId: string;
|
|
108
|
+
}): ResultAsync<PlanLaunch, LaunchPlanRunRefusal>;
|
|
58
109
|
export declare function createExecuteAnalysisTool(deps: ExecuteAnalysisToolDeps): import("./define-tool.js").Tool<{
|
|
59
110
|
mode: "plan" | "adhoc";
|
|
60
111
|
planId?: string | undefined;
|