harnery 0.13.0 → 0.14.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/README.md +1 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +2 -0
- package/dist/commands/work.d.ts +4 -0
- package/dist/commands/work.d.ts.map +1 -0
- package/dist/commands/work.js +281 -0
- package/dist/core/work/index.d.ts +3 -0
- package/dist/core/work/index.d.ts.map +1 -0
- package/dist/core/work/index.js +2 -0
- package/dist/core/work/read.d.ts +2 -0
- package/dist/core/work/read.d.ts.map +1 -0
- package/dist/core/work/read.js +1 -0
- package/dist/core/work/runner.d.ts +10 -0
- package/dist/core/work/runner.d.ts.map +1 -0
- package/dist/core/work/runner.js +63 -0
- package/dist/core/work/state.d.ts +109 -0
- package/dist/core/work/state.d.ts.map +1 -0
- package/dist/core/work/state.js +698 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +20 -1
- package/dist/core/workflow/proof.d.ts +1 -0
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +1 -0
- package/dist/core/workflow/run-state.d.ts +1 -0
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +2 -0
- package/dist/core/workflow/types.d.ts +7 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/package.json +11 -1
- package/src/commander.ts +2 -0
- package/src/commands/work.ts +367 -0
- package/src/core/work/index.ts +24 -0
- package/src/core/work/read.ts +15 -0
- package/src/core/work/runner.ts +81 -0
- package/src/core/work/state.ts +900 -0
- package/src/core/workflow/engine.ts +21 -1
- package/src/core/workflow/proof.ts +2 -0
- package/src/core/workflow/run-state.ts +3 -0
- package/src/core/workflow/types.ts +7 -0
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
policyDigest,
|
|
36
36
|
summarizePolicyRequest,
|
|
37
37
|
} from "../policy/index.ts";
|
|
38
|
-
import { createWorkflowApproval } from "./approvals.ts";
|
|
38
|
+
import { assertWorkflowRunId, createWorkflowApproval } from "./approvals.ts";
|
|
39
39
|
import { type BillingProbe, probeBilling } from "./billing.ts";
|
|
40
40
|
import {
|
|
41
41
|
buildWorkflowProof,
|
|
@@ -101,12 +101,26 @@ export class WorkflowParkedError extends Error {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
export async function runWorkflow(scriptPath: string, opts: EngineOpts): Promise<RunReport> {
|
|
104
|
+
if (opts.runId && opts.resumeRunId) {
|
|
105
|
+
throw new Error("runId and resumeRunId are mutually exclusive");
|
|
106
|
+
}
|
|
107
|
+
if (opts.runId) assertWorkflowRunId(opts.runId);
|
|
108
|
+
if (opts.workItemId && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/.test(opts.workItemId)) {
|
|
109
|
+
throw new Error(`invalid work item id ${JSON.stringify(opts.workItemId)}`);
|
|
110
|
+
}
|
|
104
111
|
if (opts.resumeRunId && opts.resumeFrom) {
|
|
105
112
|
throw new Error("resumeRunId and resumeFrom are mutually exclusive");
|
|
106
113
|
}
|
|
107
114
|
const resumeState = opts.resumeRunId
|
|
108
115
|
? assertWorkflowRunResumable(opts.coordRoot, opts.resumeRunId)
|
|
109
116
|
: undefined;
|
|
117
|
+
if (
|
|
118
|
+
resumeState !== undefined &&
|
|
119
|
+
opts.workItemId !== undefined &&
|
|
120
|
+
resumeState.manifest.work_item_id !== opts.workItemId
|
|
121
|
+
) {
|
|
122
|
+
throw new Error(`workflow run ${opts.resumeRunId} belongs to a different work item`);
|
|
123
|
+
}
|
|
110
124
|
const absScript = isAbsolute(scriptPath) ? scriptPath : resolve(process.cwd(), scriptPath);
|
|
111
125
|
if (resumeState) {
|
|
112
126
|
if (resolve(resumeState.manifest.script.path) !== resolve(absScript)) {
|
|
@@ -145,6 +159,7 @@ async function executeWorkflow(
|
|
|
145
159
|
|
|
146
160
|
const runId =
|
|
147
161
|
opts.resumeRunId ??
|
|
162
|
+
opts.runId ??
|
|
148
163
|
`wf-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}`;
|
|
149
164
|
const runDir = join(opts.coordRoot, ".harnery", "workflows", runId);
|
|
150
165
|
mkdirSync(runDir, { recursive: true });
|
|
@@ -178,6 +193,7 @@ async function executeWorkflow(
|
|
|
178
193
|
manifest: {
|
|
179
194
|
schema_version: 1,
|
|
180
195
|
run_id: runId,
|
|
196
|
+
work_item_id: opts.workItemId,
|
|
181
197
|
name,
|
|
182
198
|
started_at: startedAt,
|
|
183
199
|
script: { path: absScript, sha256: workflowScriptDigest(absScript) },
|
|
@@ -739,6 +755,7 @@ async function executeWorkflow(
|
|
|
739
755
|
} else {
|
|
740
756
|
journal("run.start", {
|
|
741
757
|
name,
|
|
758
|
+
work_item_id: opts.workItemId ?? null,
|
|
742
759
|
script: absScript,
|
|
743
760
|
objective: meta.objective ?? null,
|
|
744
761
|
acceptance: meta.acceptance,
|
|
@@ -763,6 +780,7 @@ async function executeWorkflow(
|
|
|
763
780
|
try {
|
|
764
781
|
proof = buildWorkflowProof({
|
|
765
782
|
runId,
|
|
783
|
+
workItemId: opts.workItemId ?? resumeState?.manifest.work_item_id,
|
|
766
784
|
meta,
|
|
767
785
|
status: "succeeded",
|
|
768
786
|
startedAt,
|
|
@@ -795,6 +813,7 @@ async function executeWorkflow(
|
|
|
795
813
|
}
|
|
796
814
|
const report: RunReport = {
|
|
797
815
|
runId,
|
|
816
|
+
workItemId: opts.workItemId ?? resumeState?.manifest.work_item_id,
|
|
798
817
|
name,
|
|
799
818
|
result,
|
|
800
819
|
agentsSpawned,
|
|
@@ -827,6 +846,7 @@ async function executeWorkflow(
|
|
|
827
846
|
try {
|
|
828
847
|
const proof = buildWorkflowProof({
|
|
829
848
|
runId,
|
|
849
|
+
workItemId: opts.workItemId ?? resumeState?.manifest.work_item_id,
|
|
830
850
|
meta,
|
|
831
851
|
status: "failed",
|
|
832
852
|
startedAt,
|
|
@@ -58,6 +58,7 @@ export interface NormalizedWorkflowMeta {
|
|
|
58
58
|
|
|
59
59
|
export interface BuildWorkflowProofInput {
|
|
60
60
|
runId: string;
|
|
61
|
+
workItemId?: string;
|
|
61
62
|
meta: NormalizedWorkflowMeta;
|
|
62
63
|
status: "succeeded" | "failed";
|
|
63
64
|
startedAt: string;
|
|
@@ -213,6 +214,7 @@ export function buildWorkflowProof(input: BuildWorkflowProofInput): WorkflowProo
|
|
|
213
214
|
schema_version: WORKFLOW_PROOF_SCHEMA_VERSION,
|
|
214
215
|
run: {
|
|
215
216
|
id: input.runId,
|
|
217
|
+
work_item_id: input.workItemId,
|
|
216
218
|
name: input.meta.name,
|
|
217
219
|
status: input.status,
|
|
218
220
|
started_at: input.startedAt,
|
|
@@ -31,6 +31,7 @@ const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
|
31
31
|
export interface WorkflowRunManifest {
|
|
32
32
|
schema_version: typeof WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION;
|
|
33
33
|
run_id: string;
|
|
34
|
+
work_item_id?: string;
|
|
34
35
|
name: string;
|
|
35
36
|
started_at: string;
|
|
36
37
|
script: { path: string; sha256: string };
|
|
@@ -97,6 +98,8 @@ export function readWorkflowRunManifest(coordRoot: string, runId: string): Workf
|
|
|
97
98
|
if (
|
|
98
99
|
manifest.schema_version !== WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION ||
|
|
99
100
|
manifest.run_id !== runId ||
|
|
101
|
+
(manifest.work_item_id !== undefined &&
|
|
102
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/.test(manifest.work_item_id)) ||
|
|
100
103
|
typeof manifest.name !== "string" ||
|
|
101
104
|
manifest.name.length === 0 ||
|
|
102
105
|
manifest.name.length > 200 ||
|
|
@@ -147,6 +147,8 @@ export interface WorkflowProof {
|
|
|
147
147
|
schema_version: typeof WORKFLOW_PROOF_SCHEMA_VERSION;
|
|
148
148
|
run: {
|
|
149
149
|
id: string;
|
|
150
|
+
/** Durable objective this execution attempt belongs to, when linked. */
|
|
151
|
+
work_item_id?: string;
|
|
150
152
|
name: string;
|
|
151
153
|
status: WorkflowRunStatus;
|
|
152
154
|
started_at: string;
|
|
@@ -315,6 +317,10 @@ export interface EngineOpts {
|
|
|
315
317
|
* approval has been resolved. The frozen run manifest supplies execution
|
|
316
318
|
* options and the original repository-before snapshot. */
|
|
317
319
|
resumeRunId?: string;
|
|
320
|
+
/** Stable id for a new run allocated by a durable-work host. */
|
|
321
|
+
runId?: string;
|
|
322
|
+
/** Durable objective this execution attempt belongs to. */
|
|
323
|
+
workItemId?: string;
|
|
318
324
|
/** Total-agent ceiling for the run (default 50): the runaway backstop. */
|
|
319
325
|
maxAgents?: number;
|
|
320
326
|
/** Concurrent-subagent cap for parallel() (default 4). */
|
|
@@ -357,6 +363,7 @@ export interface EngineOpts {
|
|
|
357
363
|
|
|
358
364
|
export interface RunReport {
|
|
359
365
|
runId: string;
|
|
366
|
+
workItemId?: string;
|
|
360
367
|
name: string;
|
|
361
368
|
/** What the script's default export returned. */
|
|
362
369
|
result: unknown;
|