harnery 0.19.1 → 0.21.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 -1
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +21 -1
- package/dist/core/supervisor/index.d.ts +1 -1
- package/dist/core/supervisor/index.d.ts.map +1 -1
- package/dist/core/supervisor/index.js +1 -1
- package/dist/core/supervisor/plan-api.d.ts +7 -0
- package/dist/core/supervisor/plan-api.d.ts.map +1 -1
- package/dist/core/supervisor/plan-api.js +17 -1
- package/dist/core/supervisor/plan-read.d.ts.map +1 -1
- package/dist/core/supervisor/plan-read.js +4 -0
- package/dist/core/supervisor/plan-types.d.ts +2 -2
- package/dist/core/supervisor/plan-types.d.ts.map +1 -1
- package/dist/core/supervisor/planning.d.ts +7 -0
- package/dist/core/supervisor/planning.d.ts.map +1 -1
- package/dist/core/supervisor/planning.js +38 -0
- package/dist/core/supervisor/state.d.ts +2 -1
- package/dist/core/supervisor/state.d.ts.map +1 -1
- package/dist/core/supervisor/state.js +4 -1
- package/dist/core/work/runner.d.ts +1 -1
- package/dist/core/work/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +8 -0
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +24 -4
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +29 -3
- package/dist/core/workflow/index.d.ts +2 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +1 -1
- package/dist/core/workflow/proof.d.ts +2 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +8 -1
- package/dist/core/workflow/run-state.d.ts +2 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +5 -0
- package/dist/core/workflow/types.d.ts +16 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +1 -0
- package/dist/core/workflow/work-context.d.ts +5 -0
- package/dist/core/workflow/work-context.d.ts.map +1 -0
- package/dist/core/workflow/work-context.js +58 -0
- package/package.json +1 -1
- package/src/commands/supervisor.ts +32 -0
- package/src/core/supervisor/index.ts +5 -1
- package/src/core/supervisor/plan-api.ts +27 -1
- package/src/core/supervisor/plan-read.ts +4 -0
- package/src/core/supervisor/plan-types.ts +2 -0
- package/src/core/supervisor/planning.ts +52 -0
- package/src/core/supervisor/state.ts +7 -1
- package/src/core/work/runner.ts +9 -1
- package/src/core/work/state.ts +35 -4
- package/src/core/workflow/engine.ts +33 -2
- package/src/core/workflow/index.ts +5 -1
- package/src/core/workflow/proof.ts +12 -1
- package/src/core/workflow/run-state.ts +7 -1
- package/src/core/workflow/types.ts +17 -0
- package/src/core/workflow/work-context.ts +66 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/proof.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EAEzB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EAEZ,aAAa,
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/proof.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EAEzB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EAEZ,aAAa,EAIb,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAgBpB,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAC5C,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC,CAAC;IAClF,MAAM,CAAC,EAAE;QACP,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACnC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;QACrC,SAAS,EAAE,eAAe,CAAC;QAC3B,aAAa,EAAE,mBAAmB,CAAC;KACpC,CAAC;IACF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,YAAY,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,GACnB,sBAAsB,CAqCxB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,KAAK,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,sBAAsB,CA6BzB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,QAAQ,EAAE,sBAAsB,EAAE,GACjC;IAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAuB9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,YAAY,CAQjF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,aAAa,CA4ChF;AAgCD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAe3E;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAyBjF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAoChE"}
|
|
@@ -3,6 +3,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, u
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { policyDigest } from "../policy/index.js";
|
|
5
5
|
import { WORKFLOW_PROOF_SCHEMA_VERSION } from "./types.js";
|
|
6
|
+
import { isCanonicalWorkflowWorkContext } from "./work-context.js";
|
|
6
7
|
const MAX_ACCEPTANCE_CRITERIA = 50;
|
|
7
8
|
const MAX_EVIDENCE_RECORDS = 200;
|
|
8
9
|
const MAX_PACKET_BYTES = 512 * 1024;
|
|
@@ -127,6 +128,7 @@ export function buildWorkflowProof(input) {
|
|
|
127
128
|
started_at: input.startedAt,
|
|
128
129
|
ended_at: input.endedAt,
|
|
129
130
|
duration_ms: input.durationMs,
|
|
131
|
+
work_context: input.workContext,
|
|
130
132
|
objective: input.meta.objective,
|
|
131
133
|
error: clippedOptional(input.error, MAX_SUMMARY_CHARS),
|
|
132
134
|
result: input.result === undefined ? undefined : digestResult(input.result),
|
|
@@ -209,7 +211,12 @@ export function readWorkflowProof(coordRoot, runId) {
|
|
|
209
211
|
catch (error) {
|
|
210
212
|
throw new Error(`cannot parse workflow proof at ${path}: ${error.message}`);
|
|
211
213
|
}
|
|
212
|
-
if (proof.schema_version !== WORKFLOW_PROOF_SCHEMA_VERSION ||
|
|
214
|
+
if (proof.schema_version !== WORKFLOW_PROOF_SCHEMA_VERSION ||
|
|
215
|
+
proof.run?.id !== runId ||
|
|
216
|
+
(proof.run.work_context !== undefined &&
|
|
217
|
+
(!proof.run.work_item_id ||
|
|
218
|
+
proof.run.work_context.id !== proof.run.work_item_id ||
|
|
219
|
+
!isCanonicalWorkflowWorkContext(proof.run.work_context)))) {
|
|
213
220
|
throw new Error(`workflow proof at ${path} has an unsupported or mismatched schema`);
|
|
214
221
|
}
|
|
215
222
|
return proof;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { RepoSnapshot } from "../context/index.js";
|
|
2
2
|
import { type NormalizedPolicy, type PolicyIsolation, type PolicyNetworkAccess } from "../policy/index.js";
|
|
3
|
-
import type { WorkflowSpecialistProfile } from "./types.js";
|
|
3
|
+
import type { WorkflowSpecialistProfile, WorkflowWorkContext } from "./types.js";
|
|
4
4
|
export declare const WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION: 1;
|
|
5
5
|
export interface WorkflowRunManifest {
|
|
6
6
|
schema_version: typeof WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION;
|
|
7
7
|
run_id: string;
|
|
8
8
|
work_item_id?: string;
|
|
9
|
+
work_context?: WorkflowWorkContext;
|
|
9
10
|
name: string;
|
|
10
11
|
started_at: string;
|
|
11
12
|
script: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-state.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/run-state.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"run-state.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/run-state.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGjF,eAAO,MAAM,oCAAoC,EAAG,CAAU,CAAC;AAK/D,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,oCAAoC,CAAC;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,iBAAiB,EAAE,YAAY,CAAC;IAChC,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,iBAAiB,EAAE,OAAO,CAAC;QAC3B,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;QAC/B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,SAAS,EAAE,eAAe,CAAC;QAC3B,cAAc,EAAE,mBAAmB,CAAC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;KACzD,CAAC;CACH;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,mBAAmB,CAAC;CAC/B;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAmBrF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAoC7F;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CA4BvD;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CASjF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhF;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,CA0CvF"}
|
|
@@ -5,6 +5,7 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
|
5
5
|
import { normalizePolicy, policyDigest, } from "../policy/index.js";
|
|
6
6
|
import { assertWorkflowRunId, readWorkflowApproval } from "./approvals.js";
|
|
7
7
|
import { normalizeWorkflowSpecialists } from "./specialists.js";
|
|
8
|
+
import { isCanonicalWorkflowWorkContext } from "./work-context.js";
|
|
8
9
|
export const WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION = 1;
|
|
9
10
|
const MANIFEST_LIMIT_BYTES = 256 * 1024;
|
|
10
11
|
const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
@@ -53,6 +54,10 @@ export function readWorkflowRunManifest(coordRoot, runId) {
|
|
|
53
54
|
manifest.run_id !== runId ||
|
|
54
55
|
(manifest.work_item_id !== undefined &&
|
|
55
56
|
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/.test(manifest.work_item_id)) ||
|
|
57
|
+
(manifest.work_context !== undefined &&
|
|
58
|
+
(!manifest.work_item_id ||
|
|
59
|
+
manifest.work_context.id !== manifest.work_item_id ||
|
|
60
|
+
!isCanonicalWorkflowWorkContext(manifest.work_context))) ||
|
|
56
61
|
typeof manifest.name !== "string" ||
|
|
57
62
|
manifest.name.length === 0 ||
|
|
58
63
|
manifest.name.length > 200 ||
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import type { DispatchCostEstimator, ExternalMutationRequest, NormalizedPolicy, PolicyAskResolver, PolicyDecision, PolicyIsolation, PolicyNetworkAccess, PolicySpec } from "../policy/index.js";
|
|
10
10
|
import type { BillingMode, BillingProber } from "./billing.js";
|
|
11
11
|
export declare const WORKFLOW_PROOF_SCHEMA_VERSION: 1;
|
|
12
|
+
export declare const WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION: 1;
|
|
12
13
|
export type EvidenceKind = "test" | "command" | "artifact" | "change" | "review" | "observation";
|
|
13
14
|
export type EvidenceStatus = "passed" | "failed" | "observed" | "unknown";
|
|
14
15
|
export type EvidenceSource = "workflow" | "engine";
|
|
@@ -126,6 +127,7 @@ export interface WorkflowProof {
|
|
|
126
127
|
started_at: string;
|
|
127
128
|
ended_at: string;
|
|
128
129
|
duration_ms: number;
|
|
130
|
+
work_context?: WorkflowWorkContext;
|
|
129
131
|
objective?: string;
|
|
130
132
|
error?: string;
|
|
131
133
|
result?: ResultDigest;
|
|
@@ -260,6 +262,9 @@ export type Spawner = (req: SpawnRequest) => Promise<SpawnResult>;
|
|
|
260
262
|
/** The API surface injected into a workflow script's default export. Explicit
|
|
261
263
|
* injection (no ambient globals): keeps scripts portable and unit-testable. */
|
|
262
264
|
export interface WorkflowContext {
|
|
265
|
+
/** Frozen durable-work assignment for work-linked runs. Standalone and
|
|
266
|
+
* legacy-resumed workflows have no work context. */
|
|
267
|
+
work?: Readonly<WorkflowWorkContext>;
|
|
263
268
|
/** Spawn one subagent; resolves to validated JSON (schema) or reply text. */
|
|
264
269
|
agent: (prompt: string, opts?: AgentOpts) => Promise<unknown>;
|
|
265
270
|
/** Run thunks with bounded concurrency; a rejected thunk resolves to null. */
|
|
@@ -284,6 +289,14 @@ export interface WorkflowModule {
|
|
|
284
289
|
meta?: WorkflowMeta;
|
|
285
290
|
default: (ctx: WorkflowContext) => Promise<unknown>;
|
|
286
291
|
}
|
|
292
|
+
/** Minimal immutable durable-work input exposed to reusable workflow code. */
|
|
293
|
+
export interface WorkflowWorkContext {
|
|
294
|
+
readonly schema_version: typeof WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION;
|
|
295
|
+
readonly id: string;
|
|
296
|
+
readonly title: string;
|
|
297
|
+
readonly objective: string;
|
|
298
|
+
readonly acceptance: readonly string[];
|
|
299
|
+
}
|
|
287
300
|
export interface EngineOpts {
|
|
288
301
|
/** Repo root whose .harnery/ receives the run journal. */
|
|
289
302
|
coordRoot: string;
|
|
@@ -306,6 +319,9 @@ export interface EngineOpts {
|
|
|
306
319
|
runId?: string;
|
|
307
320
|
/** Durable objective this execution attempt belongs to. */
|
|
308
321
|
workItemId?: string;
|
|
322
|
+
/** Frozen assignment data supplied by a durable-work host. Requires the
|
|
323
|
+
* matching `workItemId`; parked resume always uses the manifest copy. */
|
|
324
|
+
workContext?: WorkflowWorkContext;
|
|
309
325
|
/** Total-agent ceiling for the run (default 50): the runaway backstop. */
|
|
310
326
|
maxAgents?: number;
|
|
311
327
|
/** Concurrent-subagent cap for parallel() (default 4). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,UAAU,EACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE/D,eAAO,MAAM,6BAA6B,EAAG,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WorkflowWorkContext } from "./types.js";
|
|
2
|
+
export declare function normalizeWorkflowWorkContext(value: unknown): WorkflowWorkContext;
|
|
3
|
+
export declare function freezeWorkflowWorkContext(value: unknown): Readonly<WorkflowWorkContext>;
|
|
4
|
+
export declare function isCanonicalWorkflowWorkContext(value: unknown): value is WorkflowWorkContext;
|
|
5
|
+
//# sourceMappingURL=work-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-context.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/work-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAUtD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CA+BhF;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,mBAAmB,CAAC,CAIvF;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAO3F"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION } from "./types.js";
|
|
2
|
+
const WORK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
3
|
+
const MAX_TITLE = 200;
|
|
4
|
+
const MAX_OBJECTIVE = 4_000;
|
|
5
|
+
const MAX_ACCEPTANCE = 50;
|
|
6
|
+
const MAX_ACCEPTANCE_ITEM = 500;
|
|
7
|
+
const FIELDS = new Set(["schema_version", "id", "title", "objective", "acceptance"]);
|
|
8
|
+
export function normalizeWorkflowWorkContext(value) {
|
|
9
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
10
|
+
throw new Error("workflow work context must be an object");
|
|
11
|
+
}
|
|
12
|
+
const input = value;
|
|
13
|
+
const extra = Object.keys(input).filter((key) => !FIELDS.has(key));
|
|
14
|
+
if (extra.length > 0) {
|
|
15
|
+
throw new Error(`workflow work context has unknown field ${JSON.stringify(extra[0])}`);
|
|
16
|
+
}
|
|
17
|
+
if (input.schema_version !== WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION) {
|
|
18
|
+
throw new Error("workflow work context has an unsupported schema");
|
|
19
|
+
}
|
|
20
|
+
const id = requiredString(input.id, "workflow work context id", 100);
|
|
21
|
+
if (!WORK_ID.test(id)) {
|
|
22
|
+
throw new Error(`invalid workflow work context id ${JSON.stringify(id)}`);
|
|
23
|
+
}
|
|
24
|
+
if (!Array.isArray(input.acceptance) || input.acceptance.length > MAX_ACCEPTANCE) {
|
|
25
|
+
throw new Error(`workflow work context acceptance must contain at most ${MAX_ACCEPTANCE} criteria`);
|
|
26
|
+
}
|
|
27
|
+
const acceptance = input.acceptance.map((criterion, index) => requiredString(criterion, `workflow work context acceptance[${index}]`, MAX_ACCEPTANCE_ITEM));
|
|
28
|
+
return {
|
|
29
|
+
schema_version: WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION,
|
|
30
|
+
id,
|
|
31
|
+
title: requiredString(input.title, "workflow work context title", MAX_TITLE),
|
|
32
|
+
objective: requiredString(input.objective, "workflow work context objective", MAX_OBJECTIVE),
|
|
33
|
+
acceptance,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function freezeWorkflowWorkContext(value) {
|
|
37
|
+
const normalized = normalizeWorkflowWorkContext(value);
|
|
38
|
+
Object.freeze(normalized.acceptance);
|
|
39
|
+
return Object.freeze(normalized);
|
|
40
|
+
}
|
|
41
|
+
export function isCanonicalWorkflowWorkContext(value) {
|
|
42
|
+
try {
|
|
43
|
+
const normalized = normalizeWorkflowWorkContext(value);
|
|
44
|
+
return JSON.stringify(normalized) === JSON.stringify(value);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function requiredString(value, field, max) {
|
|
51
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
52
|
+
throw new Error(`${field} is required`);
|
|
53
|
+
}
|
|
54
|
+
const normalized = value.trim();
|
|
55
|
+
if (normalized.length > max)
|
|
56
|
+
throw new Error(`${field} exceeds ${max} characters`);
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
readSupervisorServiceStatus,
|
|
21
21
|
rejectSupervisorPlan,
|
|
22
22
|
requestSupervisorServiceStop,
|
|
23
|
+
retrySupervisorPlan,
|
|
23
24
|
runSupervisor,
|
|
24
25
|
runSupervisorServiceDaemon,
|
|
25
26
|
type SupervisorPlanOutcome,
|
|
@@ -256,6 +257,34 @@ function registerPlanCommand(supervisor: Command, emit: EmitContext): void {
|
|
|
256
257
|
});
|
|
257
258
|
},
|
|
258
259
|
);
|
|
260
|
+
|
|
261
|
+
plan
|
|
262
|
+
.command("retry <goal-id> <plan-id>")
|
|
263
|
+
.description("Attach bounded guidance and authorize a new attempt after plan attention.")
|
|
264
|
+
.requiredOption("--reason <text>", "Guidance for the next planner attempt")
|
|
265
|
+
.option("--actor <name>", "Actor recorded on the retry request")
|
|
266
|
+
.option("--json", "Emit the plan outcome as JSON")
|
|
267
|
+
.action(
|
|
268
|
+
(
|
|
269
|
+
goalId: string,
|
|
270
|
+
planId: string,
|
|
271
|
+
opts: { actor?: string; reason: string; json?: boolean },
|
|
272
|
+
) => {
|
|
273
|
+
withSupervisorRoot(emit, (coordRoot) => {
|
|
274
|
+
emitPlanOutcome(
|
|
275
|
+
retrySupervisorPlan({
|
|
276
|
+
coordRoot,
|
|
277
|
+
goalId,
|
|
278
|
+
planId,
|
|
279
|
+
actor: opts.actor,
|
|
280
|
+
reason: opts.reason,
|
|
281
|
+
}),
|
|
282
|
+
opts.json,
|
|
283
|
+
emit,
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
},
|
|
287
|
+
);
|
|
259
288
|
}
|
|
260
289
|
|
|
261
290
|
function registerServiceCommand(
|
|
@@ -723,6 +752,9 @@ function emitSupervisor(
|
|
|
723
752
|
lines.push(`original root: ${record.intent.root_work_id}`);
|
|
724
753
|
}
|
|
725
754
|
if (projection.pending_plan_id) lines.push(`pending plan: ${projection.pending_plan_id}`);
|
|
755
|
+
if (projection.attention_plan_id) {
|
|
756
|
+
lines.push(`attention plan: ${projection.attention_plan_id}`);
|
|
757
|
+
}
|
|
726
758
|
}
|
|
727
759
|
if (record.intent.mission) {
|
|
728
760
|
lines.push(`mission: ${record.intent.mission.objective}`);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { SupervisorPlanOutcome } from "./plan-types.ts";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
applySupervisorPlanProposal,
|
|
4
|
+
rejectSupervisorPlanProposal,
|
|
5
|
+
retrySupervisorPlanProposal,
|
|
6
|
+
} from "./planning.ts";
|
|
3
7
|
import { acquireSupervisorLease, readSupervisorIgnoringLease } from "./state.ts";
|
|
4
8
|
|
|
5
9
|
export function approveSupervisorPlan(input: {
|
|
@@ -45,3 +49,25 @@ export function rejectSupervisorPlan(input: {
|
|
|
45
49
|
release();
|
|
46
50
|
}
|
|
47
51
|
}
|
|
52
|
+
|
|
53
|
+
export function retrySupervisorPlan(input: {
|
|
54
|
+
coordRoot: string;
|
|
55
|
+
goalId: string;
|
|
56
|
+
planId: string;
|
|
57
|
+
actor?: string;
|
|
58
|
+
reason: string;
|
|
59
|
+
}): SupervisorPlanOutcome {
|
|
60
|
+
const actor = input.actor?.trim() || `supervisor:${input.goalId}`;
|
|
61
|
+
const release = acquireSupervisorLease(input.coordRoot, input.goalId);
|
|
62
|
+
try {
|
|
63
|
+
return retrySupervisorPlanProposal({
|
|
64
|
+
coordRoot: input.coordRoot,
|
|
65
|
+
record: readSupervisorIgnoringLease(input.coordRoot, input.goalId),
|
|
66
|
+
planId: input.planId,
|
|
67
|
+
actor,
|
|
68
|
+
reason: input.reason,
|
|
69
|
+
});
|
|
70
|
+
} finally {
|
|
71
|
+
release();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -39,6 +39,7 @@ const EVENT_TYPES = new Set<SupervisorPlanEventType>([
|
|
|
39
39
|
"plan.applied",
|
|
40
40
|
"plan.completed",
|
|
41
41
|
"plan.rejected",
|
|
42
|
+
"plan.retry_requested",
|
|
42
43
|
"plan.attention",
|
|
43
44
|
"plan.failed",
|
|
44
45
|
]);
|
|
@@ -188,6 +189,9 @@ function deriveStatus(
|
|
|
188
189
|
if (latest?.event === "plan.rejected") {
|
|
189
190
|
return { status: "rejected", work_ids: [], reason: latest.reason };
|
|
190
191
|
}
|
|
192
|
+
if (latest?.event === "plan.retry_requested") {
|
|
193
|
+
return { status: "retry_requested", work_ids: [], reason: latest.reason };
|
|
194
|
+
}
|
|
191
195
|
if (latest?.event === "plan.attention") {
|
|
192
196
|
return { status: "attention", work_ids: [], reason: latest.reason };
|
|
193
197
|
}
|
|
@@ -87,6 +87,7 @@ export type SupervisorPlanEventType =
|
|
|
87
87
|
| "plan.applied"
|
|
88
88
|
| "plan.completed"
|
|
89
89
|
| "plan.rejected"
|
|
90
|
+
| "plan.retry_requested"
|
|
90
91
|
| "plan.attention"
|
|
91
92
|
| "plan.failed";
|
|
92
93
|
|
|
@@ -154,6 +155,7 @@ export type SupervisorPlanStatus =
|
|
|
154
155
|
| "applied"
|
|
155
156
|
| "completed"
|
|
156
157
|
| "rejected"
|
|
158
|
+
| "retry_requested"
|
|
157
159
|
| "attention"
|
|
158
160
|
| "failed";
|
|
159
161
|
|
|
@@ -583,6 +583,58 @@ export function rejectSupervisorPlanProposal(input: {
|
|
|
583
583
|
return outcome(readSupervisorPlan(input.coordRoot, input.goalId, input.planId));
|
|
584
584
|
}
|
|
585
585
|
|
|
586
|
+
export function retrySupervisorPlanProposal(input: {
|
|
587
|
+
coordRoot: string;
|
|
588
|
+
record: SupervisorRecord;
|
|
589
|
+
planId: string;
|
|
590
|
+
actor: string;
|
|
591
|
+
reason: string;
|
|
592
|
+
}): SupervisorPlanOutcome {
|
|
593
|
+
const policy = input.record.intent.replanning;
|
|
594
|
+
if (!policy) {
|
|
595
|
+
throw new Error(`supervisor ${input.record.intent.id} does not allow replanning`);
|
|
596
|
+
}
|
|
597
|
+
const reason = bounded(input.reason, "plan retry reason", MAX_REASON);
|
|
598
|
+
const history = readSupervisorPlans(
|
|
599
|
+
input.coordRoot,
|
|
600
|
+
input.record.intent.id,
|
|
601
|
+
input.record.intent.root_work_id,
|
|
602
|
+
);
|
|
603
|
+
const latest = history.latest;
|
|
604
|
+
if (!latest || latest.request.id !== input.planId) {
|
|
605
|
+
throw new Error(`supervisor plan ${input.planId} is not the latest plan`);
|
|
606
|
+
}
|
|
607
|
+
const triggerFingerprint = supervisorGraphFingerprint({
|
|
608
|
+
rootWorkId: history.active_root_work_id,
|
|
609
|
+
generation: history.generation,
|
|
610
|
+
work: input.record.work,
|
|
611
|
+
});
|
|
612
|
+
if (
|
|
613
|
+
latest.request.trigger_fingerprint !== triggerFingerprint ||
|
|
614
|
+
latest.request.prior_root_work_id !== history.active_root_work_id
|
|
615
|
+
) {
|
|
616
|
+
throw new Error(`supervisor plan ${input.planId} no longer matches the active graph`);
|
|
617
|
+
}
|
|
618
|
+
if (history.completed) {
|
|
619
|
+
throw new Error(`supervisor ${input.record.intent.id} mission is already complete`);
|
|
620
|
+
}
|
|
621
|
+
if (latest.status === "retry_requested") return outcome(latest);
|
|
622
|
+
if (latest.status !== "attention") {
|
|
623
|
+
throw new Error(`supervisor plan ${input.planId} cannot be retried from ${latest.status}`);
|
|
624
|
+
}
|
|
625
|
+
if (history.plans.length >= policy.max_replans) {
|
|
626
|
+
throw new Error(
|
|
627
|
+
`supervisor ${input.record.intent.id} exhausted its ${policy.max_replans} replans`,
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
appendPlanEvent(input.coordRoot, input.record.intent.id, input.planId, {
|
|
631
|
+
event: "plan.retry_requested",
|
|
632
|
+
actor: input.actor,
|
|
633
|
+
reason,
|
|
634
|
+
});
|
|
635
|
+
return outcome(readSupervisorPlan(input.coordRoot, input.record.intent.id, input.planId));
|
|
636
|
+
}
|
|
637
|
+
|
|
586
638
|
function createPlanRequest(
|
|
587
639
|
coordRoot: string,
|
|
588
640
|
record: SupervisorRecord,
|
|
@@ -97,6 +97,7 @@ export type SupervisorNextAction =
|
|
|
97
97
|
| "plan_initial"
|
|
98
98
|
| "plan_milestone"
|
|
99
99
|
| "review_plan"
|
|
100
|
+
| "retry_plan"
|
|
100
101
|
| "none";
|
|
101
102
|
|
|
102
103
|
export interface SupervisorProjection {
|
|
@@ -121,6 +122,7 @@ export interface SupervisorProjection {
|
|
|
121
122
|
milestones_completed: number;
|
|
122
123
|
milestones_remaining: number;
|
|
123
124
|
pending_plan_id?: string;
|
|
125
|
+
attention_plan_id?: string;
|
|
124
126
|
latest_plan_status?: SupervisorPlanRecord["status"];
|
|
125
127
|
governed_work_ids: string[];
|
|
126
128
|
}
|
|
@@ -445,7 +447,11 @@ function deriveProjection(
|
|
|
445
447
|
...base,
|
|
446
448
|
state: "awaiting_attention",
|
|
447
449
|
reason: plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`,
|
|
448
|
-
|
|
450
|
+
attention_plan_id: plans.latest.request.id,
|
|
451
|
+
next_action:
|
|
452
|
+
intent.replanning && plans.plans.length < intent.replanning.max_replans
|
|
453
|
+
? "retry_plan"
|
|
454
|
+
: "none",
|
|
449
455
|
};
|
|
450
456
|
}
|
|
451
457
|
if (!root && intent.mission) {
|
package/src/core/work/runner.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { runWorkflow } from "../workflow/engine.ts";
|
|
3
3
|
import type { EngineOpts, RunReport } from "../workflow/types.ts";
|
|
4
|
+
import { WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION } from "../workflow/types.ts";
|
|
4
5
|
import {
|
|
5
6
|
acquireWorkLease,
|
|
6
7
|
appendReconcileIfChanged,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
export interface RunWorkItemInput {
|
|
15
16
|
coordRoot: string;
|
|
16
17
|
workId: string;
|
|
17
|
-
engine: Omit<EngineOpts, "coordRoot" | "runId" | "resumeRunId" | "workItemId">;
|
|
18
|
+
engine: Omit<EngineOpts, "coordRoot" | "runId" | "resumeRunId" | "workItemId" | "workContext">;
|
|
18
19
|
retry?: boolean;
|
|
19
20
|
actor?: string;
|
|
20
21
|
}
|
|
@@ -69,6 +70,13 @@ export async function runWorkItem(input: RunWorkItemInput): Promise<RunReport> {
|
|
|
69
70
|
coordRoot,
|
|
70
71
|
runId,
|
|
71
72
|
workItemId: input.workId,
|
|
73
|
+
workContext: {
|
|
74
|
+
schema_version: WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION,
|
|
75
|
+
id: record.intent.id,
|
|
76
|
+
title: record.intent.title,
|
|
77
|
+
objective: record.intent.objective,
|
|
78
|
+
acceptance: [...record.intent.acceptance],
|
|
79
|
+
},
|
|
72
80
|
});
|
|
73
81
|
} finally {
|
|
74
82
|
try {
|
package/src/core/work/state.ts
CHANGED
|
@@ -317,7 +317,7 @@ function deriveWorkProjection(
|
|
|
317
317
|
const terminal =
|
|
318
318
|
latestGovernance?.event === "work.accepted" || latestGovernance?.event === "work.cancelled";
|
|
319
319
|
const attemptEvents = events.filter((event) => event.event === "attempt.started");
|
|
320
|
-
const attempts = attemptEvents.map((event) => inspectAttempt(coordRoot, event));
|
|
320
|
+
const attempts = attemptEvents.map((event) => inspectAttempt(coordRoot, event, intent));
|
|
321
321
|
const base = {
|
|
322
322
|
id: intent.id,
|
|
323
323
|
title: intent.title,
|
|
@@ -342,7 +342,7 @@ function deriveWorkProjection(
|
|
|
342
342
|
const reopenSeq = latestGovernance?.event === "work.reopened" ? latestGovernance.seq : 0;
|
|
343
343
|
const currentAttempts = attemptEvents
|
|
344
344
|
.filter((event) => event.seq > reopenSeq)
|
|
345
|
-
.map((event) => inspectAttempt(coordRoot, event));
|
|
345
|
+
.map((event) => inspectAttempt(coordRoot, event, intent));
|
|
346
346
|
const unresolved = intent.dependencies.filter((dependency) => {
|
|
347
347
|
try {
|
|
348
348
|
return readWorkItem(coordRoot, dependency).projection.state !== "succeeded";
|
|
@@ -419,7 +419,7 @@ function deriveWorkProjection(
|
|
|
419
419
|
};
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
-
function inspectAttempt(coordRoot: string, event: WorkEvent): WorkAttempt {
|
|
422
|
+
function inspectAttempt(coordRoot: string, event: WorkEvent, intent: WorkIntent): WorkAttempt {
|
|
423
423
|
const runId = event.run_id as string;
|
|
424
424
|
const attempt: WorkAttempt = {
|
|
425
425
|
number: event.attempt as number,
|
|
@@ -429,17 +429,32 @@ function inspectAttempt(coordRoot: string, event: WorkEvent): WorkAttempt {
|
|
|
429
429
|
};
|
|
430
430
|
const proofPath = join(coordRoot, ".harnery", "workflows", runId, "proof.json");
|
|
431
431
|
const manifestPath = join(coordRoot, ".harnery", "workflows", runId, "run.json");
|
|
432
|
+
let manifest: ReturnType<typeof readWorkflowRunManifest> | undefined;
|
|
432
433
|
if (existsSync(manifestPath)) {
|
|
433
|
-
|
|
434
|
+
manifest = readWorkflowRunManifest(coordRoot, runId);
|
|
434
435
|
if (manifest.work_item_id !== event.work_id) {
|
|
435
436
|
throw new Error(`workflow run ${runId} does not belong to work item ${event.work_id}`);
|
|
436
437
|
}
|
|
438
|
+
if (
|
|
439
|
+
manifest.work_context !== undefined &&
|
|
440
|
+
JSON.stringify(manifest.work_context) !== JSON.stringify(workContextForIntent(intent))
|
|
441
|
+
) {
|
|
442
|
+
throw new Error(
|
|
443
|
+
`workflow run ${runId} work context does not match work item ${event.work_id}`,
|
|
444
|
+
);
|
|
445
|
+
}
|
|
437
446
|
}
|
|
438
447
|
if (existsSync(proofPath)) {
|
|
439
448
|
if (!existsSync(manifestPath)) {
|
|
440
449
|
throw new Error(`workflow run ${runId} has proof without a work-linked manifest`);
|
|
441
450
|
}
|
|
442
451
|
const proof = readWorkflowProof(coordRoot, runId);
|
|
452
|
+
if (proof.run.work_item_id !== event.work_id) {
|
|
453
|
+
throw new Error(`workflow proof ${runId} does not belong to work item ${event.work_id}`);
|
|
454
|
+
}
|
|
455
|
+
if (JSON.stringify(proof.run.work_context) !== JSON.stringify(manifest?.work_context)) {
|
|
456
|
+
throw new Error(`workflow proof ${runId} work context does not match its run manifest`);
|
|
457
|
+
}
|
|
443
458
|
attempt.proof_path = proofPath;
|
|
444
459
|
attempt.status =
|
|
445
460
|
proof.run.status === "succeeded" &&
|
|
@@ -471,6 +486,22 @@ function inspectAttempt(coordRoot: string, event: WorkEvent): WorkAttempt {
|
|
|
471
486
|
return attempt;
|
|
472
487
|
}
|
|
473
488
|
|
|
489
|
+
function workContextForIntent(intent: WorkIntent): {
|
|
490
|
+
schema_version: 1;
|
|
491
|
+
id: string;
|
|
492
|
+
title: string;
|
|
493
|
+
objective: string;
|
|
494
|
+
acceptance: string[];
|
|
495
|
+
} {
|
|
496
|
+
return {
|
|
497
|
+
schema_version: 1,
|
|
498
|
+
id: intent.id,
|
|
499
|
+
title: intent.title,
|
|
500
|
+
objective: intent.objective,
|
|
501
|
+
acceptance: [...intent.acceptance],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
474
505
|
function appendGovernanceEvent(
|
|
475
506
|
coordRoot: string,
|
|
476
507
|
workId: string,
|