harnery 0.16.0 → 0.18.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 +223 -5
- package/dist/core/supervisor/index.d.ts +4 -1
- package/dist/core/supervisor/index.d.ts.map +1 -1
- package/dist/core/supervisor/index.js +3 -0
- package/dist/core/supervisor/plan-api.d.ts +16 -0
- package/dist/core/supervisor/plan-api.d.ts.map +1 -0
- package/dist/core/supervisor/plan-api.js +34 -0
- package/dist/core/supervisor/plan-read.d.ts +4 -0
- package/dist/core/supervisor/plan-read.d.ts.map +1 -0
- package/dist/core/supervisor/plan-read.js +315 -0
- package/dist/core/supervisor/plan-types.d.ts +123 -0
- package/dist/core/supervisor/plan-types.d.ts.map +1 -0
- package/dist/core/supervisor/plan-types.js +14 -0
- package/dist/core/supervisor/planning.d.ts +26 -0
- package/dist/core/supervisor/planning.d.ts.map +1 -0
- package/dist/core/supervisor/planning.js +579 -0
- package/dist/core/supervisor/read.d.ts +3 -1
- package/dist/core/supervisor/read.d.ts.map +1 -1
- package/dist/core/supervisor/read.js +2 -0
- package/dist/core/supervisor/runner.d.ts +3 -0
- package/dist/core/supervisor/runner.d.ts.map +1 -1
- package/dist/core/supervisor/runner.js +42 -7
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +10 -0
- package/dist/core/supervisor/state.d.ts +27 -2
- package/dist/core/supervisor/state.d.ts.map +1 -1
- package/dist/core/supervisor/state.js +302 -13
- package/package.json +1 -1
- package/src/commands/supervisor.ts +275 -5
- package/src/core/supervisor/index.ts +21 -0
- package/src/core/supervisor/plan-api.ts +47 -0
- package/src/core/supervisor/plan-read.ts +356 -0
- package/src/core/supervisor/plan-types.ts +157 -0
- package/src/core/supervisor/planning.ts +698 -0
- package/src/core/supervisor/read.ts +17 -0
- package/src/core/supervisor/runner.ts +46 -2
- package/src/core/supervisor/service.ts +10 -0
- package/src/core/supervisor/state.ts +389 -12
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { acceptWorkItem, reconcileWorkItem, runWorkItem, } from "../work/index.js";
|
|
3
|
+
import { runSupervisorPlanner } from "./planning.js";
|
|
3
4
|
import { acquireSupervisorLease, readSupervisorIgnoringLease, } from "./state.js";
|
|
4
5
|
export async function runSupervisor(input) {
|
|
5
6
|
const coordRoot = resolve(input.coordRoot);
|
|
@@ -12,7 +13,9 @@ export async function runSupervisor(input) {
|
|
|
12
13
|
let cycles = 0;
|
|
13
14
|
let dispatches = 0;
|
|
14
15
|
let acceptances = 0;
|
|
16
|
+
let replans = 0;
|
|
15
17
|
const outcomes = [];
|
|
18
|
+
const planOutcomes = [];
|
|
16
19
|
try {
|
|
17
20
|
const initial = readSupervisorIgnoringLease(coordRoot, input.goalId);
|
|
18
21
|
const cycleLimit = mode === "tick" ? 1 : initial.intent.limits.max_cycles;
|
|
@@ -30,6 +33,31 @@ export async function runSupervisor(input) {
|
|
|
30
33
|
}
|
|
31
34
|
const before = graphFingerprint(record);
|
|
32
35
|
let changed = false;
|
|
36
|
+
if (record.projection.next_action === "replan" ||
|
|
37
|
+
record.projection.next_action === "plan_initial" ||
|
|
38
|
+
record.projection.next_action === "plan_milestone") {
|
|
39
|
+
log(`[${record.intent.title}] ${record.projection.next_action} at graph generation ${record.projection.plan_generation}`);
|
|
40
|
+
const plan = await runSupervisorPlanner({
|
|
41
|
+
coordRoot,
|
|
42
|
+
record,
|
|
43
|
+
engine: input.engine,
|
|
44
|
+
actor,
|
|
45
|
+
onLog: input.onLog,
|
|
46
|
+
});
|
|
47
|
+
replans++;
|
|
48
|
+
planOutcomes.push(plan);
|
|
49
|
+
record = refreshGraph(coordRoot, input.goalId, actor);
|
|
50
|
+
if (mode === "tick") {
|
|
51
|
+
return report("tick_complete", `planner produced ${plan.status} plan ${plan.plan_id}`, record);
|
|
52
|
+
}
|
|
53
|
+
if (record.projection.state === "succeeded") {
|
|
54
|
+
return report("succeeded", record.projection.reason, record);
|
|
55
|
+
}
|
|
56
|
+
if (record.projection.state !== "ready") {
|
|
57
|
+
return report(stopForProjection(record.projection), record.projection.reason, record);
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
33
61
|
if (record.intent.automation.accept_passing_proof) {
|
|
34
62
|
for (const work of record.work) {
|
|
35
63
|
if (work.projection.state !== "in_review")
|
|
@@ -133,7 +161,9 @@ export async function runSupervisor(input) {
|
|
|
133
161
|
cycles,
|
|
134
162
|
dispatches,
|
|
135
163
|
acceptances,
|
|
164
|
+
replans,
|
|
136
165
|
outcomes,
|
|
166
|
+
plan_outcomes: planOutcomes,
|
|
137
167
|
projection: record.projection,
|
|
138
168
|
};
|
|
139
169
|
}
|
|
@@ -177,13 +207,18 @@ function selectWithinAttemptBudget(record, candidates) {
|
|
|
177
207
|
});
|
|
178
208
|
}
|
|
179
209
|
function graphFingerprint(record) {
|
|
180
|
-
return JSON.stringify(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
work.
|
|
185
|
-
|
|
186
|
-
|
|
210
|
+
return JSON.stringify([
|
|
211
|
+
record.projection.root_work_id,
|
|
212
|
+
record.projection.plan_generation,
|
|
213
|
+
record.projection.latest_plan_status,
|
|
214
|
+
record.work.map((work) => [
|
|
215
|
+
work.intent.id,
|
|
216
|
+
work.projection.state,
|
|
217
|
+
work.projection.next_action,
|
|
218
|
+
work.projection.attempts_used,
|
|
219
|
+
work.events.length,
|
|
220
|
+
]),
|
|
221
|
+
]);
|
|
187
222
|
}
|
|
188
223
|
function stopForProjection(projection) {
|
|
189
224
|
if (projection.state === "budget_exhausted")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/service.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,gBAAgB,EAAmB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAClG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAiB,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnE,eAAO,MAAM,wCAAwC,EAAG,CAAU,CAAC;AACnE,eAAO,MAAM,yCAAyC,EAAG,CAAU,CAAC;AACpE,eAAO,MAAM,wCAAwC,EAAG,CAAU,CAAC;AAOnE,MAAM,WAAW,6BAA6B;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,OAAO,wCAAwC,CAAC;IAChE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,6BAA6B,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;CACjD;AAED,MAAM,MAAM,0BAA0B,GAAG,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAAC;AAEhF,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,0BAA0B,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,OAAO,yCAAyC,CAAC;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,4BAA4B,CAAC,CAAC;CACrD;AAED,MAAM,MAAM,6BAA6B,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAE5F,MAAM,WAAW,6BAA6B;IAC5C,cAAc,EAAE,OAAO,wCAAwC,CAAC;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,6BAA6B,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,6BAA6B,CAAC;IACvC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,6BAA6B,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,uBAAuB,CAAC;IAChC,MAAM,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,gBAAgB,CAAC;IACnE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC3D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AASD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,uBAAuB,CAiDzB;AAED,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,uBAAuB,CAQzF;AAED,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,MAAM,GACnB,wBAAwB,GAAG,SAAS,CAkBtC;AAED,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,uBAAuB,CAkBzF;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,uBAAuB,CAAC,CAwClC;AAED,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,uBAAuB,CAgB1F;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,4BAA4B,CAAC,CAwIvC;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,+BAA+B,GACrC,OAAO,CAAC,6BAA6B,CAAC,CAyGxC;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAElE"}
|
|
@@ -241,6 +241,7 @@ export async function runSupervisorServiceSweep(input) {
|
|
|
241
241
|
cycles: report.cycles,
|
|
242
242
|
dispatches: report.dispatches,
|
|
243
243
|
acceptances: report.acceptances,
|
|
244
|
+
replans: report.replans,
|
|
244
245
|
});
|
|
245
246
|
persistRuntime(coordRoot, runtime, completedAt);
|
|
246
247
|
}
|
|
@@ -432,6 +433,15 @@ function projectionFingerprint(projection) {
|
|
|
432
433
|
projection.next_action,
|
|
433
434
|
projection.attempts_used,
|
|
434
435
|
projection.attempts_remaining,
|
|
436
|
+
projection.root_work_id,
|
|
437
|
+
projection.root_materialized,
|
|
438
|
+
projection.plan_generation,
|
|
439
|
+
projection.replans_used,
|
|
440
|
+
projection.replans_remaining,
|
|
441
|
+
projection.milestones_completed,
|
|
442
|
+
projection.milestones_remaining,
|
|
443
|
+
projection.pending_plan_id,
|
|
444
|
+
projection.latest_plan_status,
|
|
435
445
|
projection.ready_work,
|
|
436
446
|
projection.resumable_work,
|
|
437
447
|
projection.retryable_work,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type WorkRecord } from "../work/read.js";
|
|
2
2
|
import type { WorkflowSpecialistProfile } from "../workflow/types.js";
|
|
3
|
+
import { type CreateSupervisorReplanningInput, type SupervisorPlanRecord, type SupervisorReplanningPolicy } from "./plan-types.js";
|
|
3
4
|
export declare const SUPERVISOR_INTENT_SCHEMA_VERSION: 1;
|
|
4
5
|
export interface SupervisorLimits {
|
|
5
6
|
max_cycles: number;
|
|
@@ -14,6 +15,16 @@ export interface SupervisorAutomationPolicy {
|
|
|
14
15
|
resume_approved: boolean;
|
|
15
16
|
retry_blocked: boolean;
|
|
16
17
|
}
|
|
18
|
+
export interface SupervisorMission {
|
|
19
|
+
objective: string;
|
|
20
|
+
acceptance: string[];
|
|
21
|
+
max_milestones: number;
|
|
22
|
+
}
|
|
23
|
+
export interface CreateSupervisorMissionInput {
|
|
24
|
+
objective: string;
|
|
25
|
+
acceptance: readonly string[];
|
|
26
|
+
maxMilestones?: number;
|
|
27
|
+
}
|
|
17
28
|
export interface SupervisorIntent {
|
|
18
29
|
schema_version: typeof SUPERVISOR_INTENT_SCHEMA_VERSION;
|
|
19
30
|
id: string;
|
|
@@ -22,14 +33,17 @@ export interface SupervisorIntent {
|
|
|
22
33
|
specialists: Record<string, WorkflowSpecialistProfile>;
|
|
23
34
|
limits: SupervisorLimits;
|
|
24
35
|
automation: SupervisorAutomationPolicy;
|
|
36
|
+
mission?: SupervisorMission;
|
|
37
|
+
replanning?: SupervisorReplanningPolicy;
|
|
25
38
|
created_at: string;
|
|
26
39
|
}
|
|
27
40
|
export type SupervisorState = "ready" | "running" | "awaiting_attention" | "blocked" | "budget_exhausted" | "succeeded";
|
|
28
|
-
export type SupervisorNextAction = "run" | "wait_for_run" | "resolve_approval" | "review" | "retry" | "none";
|
|
41
|
+
export type SupervisorNextAction = "run" | "wait_for_run" | "resolve_approval" | "review" | "retry" | "replan" | "plan_initial" | "plan_milestone" | "review_plan" | "none";
|
|
29
42
|
export interface SupervisorProjection {
|
|
30
43
|
id: string;
|
|
31
44
|
title: string;
|
|
32
45
|
root_work_id: string;
|
|
46
|
+
root_materialized: boolean;
|
|
33
47
|
state: SupervisorState;
|
|
34
48
|
reason: string;
|
|
35
49
|
next_action: SupervisorNextAction;
|
|
@@ -41,20 +55,31 @@ export interface SupervisorProjection {
|
|
|
41
55
|
attempts_used: number;
|
|
42
56
|
attempts_remaining: number;
|
|
43
57
|
specialists: string[];
|
|
58
|
+
plan_generation: number;
|
|
59
|
+
replans_used: number;
|
|
60
|
+
replans_remaining: number;
|
|
61
|
+
milestones_completed: number;
|
|
62
|
+
milestones_remaining: number;
|
|
63
|
+
pending_plan_id?: string;
|
|
64
|
+
latest_plan_status?: SupervisorPlanRecord["status"];
|
|
65
|
+
governed_work_ids: string[];
|
|
44
66
|
}
|
|
45
67
|
export interface SupervisorRecord {
|
|
46
68
|
intent: SupervisorIntent;
|
|
47
69
|
projection: SupervisorProjection;
|
|
48
70
|
work: WorkRecord[];
|
|
71
|
+
plans: SupervisorPlanRecord[];
|
|
49
72
|
}
|
|
50
73
|
export interface CreateSupervisorInput {
|
|
51
74
|
coordRoot: string;
|
|
52
|
-
rootWorkId
|
|
75
|
+
rootWorkId?: string;
|
|
53
76
|
specialists: Readonly<Record<string, WorkflowSpecialistProfile>>;
|
|
54
77
|
title?: string;
|
|
55
78
|
id?: string;
|
|
56
79
|
limits?: Partial<SupervisorLimits>;
|
|
57
80
|
automation?: Partial<SupervisorAutomationPolicy>;
|
|
81
|
+
mission?: CreateSupervisorMissionInput;
|
|
82
|
+
replanning?: CreateSupervisorReplanningInput;
|
|
58
83
|
}
|
|
59
84
|
export declare function createSupervisor(input: CreateSupervisorInput): SupervisorRecord;
|
|
60
85
|
export declare function readSupervisor(coordRoot: string, goalId: string): SupervisorRecord;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/state.ts"],"names":[],"mappings":"AAeA,OAAO,EAA8B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,28 +1,54 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
5
|
import { assertWorkId, readWorkItem } from "../work/read.js";
|
|
6
|
+
import { workflowScriptDigest } from "../workflow/run-state.js";
|
|
6
7
|
import { normalizeWorkflowSpecialists } from "../workflow/specialists.js";
|
|
8
|
+
import { readSupervisorPlans } from "./plan-read.js";
|
|
9
|
+
import { supervisorGraphFingerprint, } from "./plan-types.js";
|
|
7
10
|
export const SUPERVISOR_INTENT_SCHEMA_VERSION = 1;
|
|
8
11
|
const GOAL_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
9
12
|
const MAX_TITLE = 200;
|
|
13
|
+
const MAX_OBJECTIVE = 4_000;
|
|
14
|
+
const MAX_ACCEPTANCE = 50;
|
|
10
15
|
const MAX_INTENT_BYTES = 256 * 1024;
|
|
16
|
+
const MAX_TEMPLATES = 20;
|
|
17
|
+
const TEMPLATE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
11
18
|
const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
12
19
|
export function createSupervisor(input) {
|
|
13
20
|
const coordRoot = resolve(input.coordRoot);
|
|
14
|
-
assertWorkId(input.rootWorkId);
|
|
15
|
-
const root = readWorkItem(coordRoot, input.rootWorkId);
|
|
16
21
|
const id = input.id ?? newSupervisorId();
|
|
17
22
|
assertSupervisorId(id);
|
|
23
|
+
const mission = input.mission ? normalizeMission(input.mission) : undefined;
|
|
24
|
+
if (mission && !input.replanning) {
|
|
25
|
+
throw new Error("supervisor mission requires a replanning policy");
|
|
26
|
+
}
|
|
27
|
+
if (!input.rootWorkId && !mission) {
|
|
28
|
+
throw new Error("supervisor creation without root work requires a mission and replanning policy");
|
|
29
|
+
}
|
|
30
|
+
if (input.rootWorkId)
|
|
31
|
+
assertWorkId(input.rootWorkId);
|
|
32
|
+
const root = input.rootWorkId ? readWorkItem(coordRoot, input.rootWorkId) : undefined;
|
|
33
|
+
const replanning = input.replanning
|
|
34
|
+
? normalizeReplanning(input.replanning, input.specialists)
|
|
35
|
+
: undefined;
|
|
36
|
+
if (mission &&
|
|
37
|
+
!input.rootWorkId &&
|
|
38
|
+
replanning &&
|
|
39
|
+
replanning.max_replans <= mission.max_milestones) {
|
|
40
|
+
throw new Error("objective-first mission max_replans must exceed max_milestones to reserve completion review");
|
|
41
|
+
}
|
|
18
42
|
const intent = {
|
|
19
43
|
schema_version: SUPERVISOR_INTENT_SCHEMA_VERSION,
|
|
20
44
|
id,
|
|
21
|
-
title: bounded(input.title ?? root
|
|
22
|
-
root_work_id: input.rootWorkId,
|
|
45
|
+
title: bounded(input.title ?? root?.intent.title ?? missionTitle(mission), "supervisor title", MAX_TITLE),
|
|
46
|
+
root_work_id: input.rootWorkId ?? missionRootId(id),
|
|
23
47
|
specialists: normalizeWorkflowSpecialists(input.specialists),
|
|
24
48
|
limits: normalizeLimits(input.limits),
|
|
25
49
|
automation: normalizeAutomation(input.automation),
|
|
50
|
+
...(mission ? { mission } : {}),
|
|
51
|
+
...(replanning ? { replanning } : {}),
|
|
26
52
|
created_at: new Date().toISOString(),
|
|
27
53
|
};
|
|
28
54
|
const path = supervisorIntentPath(coordRoot, id);
|
|
@@ -126,18 +152,23 @@ export function acquireSupervisorLease(coordRoot, goalId) {
|
|
|
126
152
|
function readSupervisorInternal(coordRootRaw, goalId, ignoreLease) {
|
|
127
153
|
const coordRoot = resolve(coordRootRaw);
|
|
128
154
|
const intent = readSupervisorIntent(coordRoot, goalId);
|
|
129
|
-
const
|
|
155
|
+
const plans = readSupervisorPlans(coordRoot, intent.id, intent.root_work_id);
|
|
156
|
+
const activeRootExists = workIntentExists(coordRoot, plans.active_root_work_id);
|
|
157
|
+
if (!activeRootExists && (!intent.mission || plans.generation > 0)) {
|
|
158
|
+
throw new Error(`supervisor ${intent.id} root work is missing`);
|
|
159
|
+
}
|
|
160
|
+
const work = activeRootExists ? collectSupervisorWork(coordRoot, plans.active_root_work_id) : [];
|
|
161
|
+
const governed = collectGovernedWork(coordRoot, intent.root_work_id, plans, work);
|
|
130
162
|
return {
|
|
131
163
|
intent,
|
|
132
164
|
work,
|
|
133
|
-
|
|
165
|
+
plans: plans.plans,
|
|
166
|
+
projection: deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease),
|
|
134
167
|
};
|
|
135
168
|
}
|
|
136
|
-
function deriveProjection(coordRoot, intent, work, ignoreLease) {
|
|
137
|
-
const root = work.find((record) => record.intent.id ===
|
|
138
|
-
|
|
139
|
-
throw new Error(`supervisor ${intent.id} root work is missing`);
|
|
140
|
-
const attemptsUsed = work.reduce((sum, record) => sum + record.projection.attempts_used, 0);
|
|
169
|
+
function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease) {
|
|
170
|
+
const root = work.find((record) => record.intent.id === plans.active_root_work_id);
|
|
171
|
+
const attemptsUsed = governed.reduce((sum, record) => sum + record.projection.attempts_used, 0);
|
|
141
172
|
const readyWork = work
|
|
142
173
|
.filter((record) => record.projection.state === "ready")
|
|
143
174
|
.map((record) => record.intent.id);
|
|
@@ -169,10 +200,14 @@ function deriveProjection(coordRoot, intent, work, ignoreLease) {
|
|
|
169
200
|
...cancelled,
|
|
170
201
|
...terminalBlocked,
|
|
171
202
|
]);
|
|
203
|
+
const initialRootAccepted = intent.mission !== undefined &&
|
|
204
|
+
governed.some((record) => record.intent.id === intent.root_work_id && record.projection.state === "succeeded");
|
|
205
|
+
const milestonesCompleted = plans.milestones_completed + (initialRootAccepted ? 1 : 0);
|
|
172
206
|
const base = {
|
|
173
207
|
id: intent.id,
|
|
174
208
|
title: intent.title,
|
|
175
|
-
root_work_id:
|
|
209
|
+
root_work_id: plans.active_root_work_id,
|
|
210
|
+
root_materialized: root !== undefined,
|
|
176
211
|
work_ids: work.map((record) => record.intent.id),
|
|
177
212
|
ready_work: readyWork,
|
|
178
213
|
resumable_work: resumableWork,
|
|
@@ -181,6 +216,14 @@ function deriveProjection(coordRoot, intent, work, ignoreLease) {
|
|
|
181
216
|
attempts_used: attemptsUsed,
|
|
182
217
|
attempts_remaining: Math.max(0, intent.limits.max_total_attempts - attemptsUsed),
|
|
183
218
|
specialists: Object.keys(intent.specialists),
|
|
219
|
+
plan_generation: plans.generation,
|
|
220
|
+
replans_used: plans.plans.length,
|
|
221
|
+
replans_remaining: Math.max(0, (intent.replanning?.max_replans ?? 0) - plans.plans.length),
|
|
222
|
+
milestones_completed: milestonesCompleted,
|
|
223
|
+
milestones_remaining: Math.max(0, (intent.mission?.max_milestones ?? 0) - milestonesCompleted),
|
|
224
|
+
pending_plan_id: plans.latest?.status === "proposed" ? plans.latest.request.id : undefined,
|
|
225
|
+
latest_plan_status: plans.latest?.status,
|
|
226
|
+
governed_work_ids: governed.map((record) => record.intent.id),
|
|
184
227
|
};
|
|
185
228
|
if (!ignoreLease && supervisorLeaseIsLive(coordRoot, intent.id)) {
|
|
186
229
|
return {
|
|
@@ -190,6 +233,88 @@ function deriveProjection(coordRoot, intent, work, ignoreLease) {
|
|
|
190
233
|
next_action: "wait_for_run",
|
|
191
234
|
};
|
|
192
235
|
}
|
|
236
|
+
if (intent.mission && plans.completed) {
|
|
237
|
+
return {
|
|
238
|
+
...base,
|
|
239
|
+
state: "succeeded",
|
|
240
|
+
reason: "mission completion was explicitly accepted",
|
|
241
|
+
next_action: "none",
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (plans.latest?.status === "proposed") {
|
|
245
|
+
return {
|
|
246
|
+
...base,
|
|
247
|
+
state: "awaiting_attention",
|
|
248
|
+
reason: `replacement plan ${plans.latest.request.id} awaits explicit review`,
|
|
249
|
+
next_action: "review_plan",
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (plans.latest?.status === "awaiting_approval") {
|
|
253
|
+
return {
|
|
254
|
+
...base,
|
|
255
|
+
state: "awaiting_attention",
|
|
256
|
+
reason: `planner workflow awaits approval ${plans.latest.approval_id}`,
|
|
257
|
+
next_action: "resolve_approval",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
if (plans.latest?.status === "resumable") {
|
|
261
|
+
return {
|
|
262
|
+
...base,
|
|
263
|
+
state: "ready",
|
|
264
|
+
reason: `planner workflow ${plans.latest.request.workflow_run_id} is resumable`,
|
|
265
|
+
next_action: "replan",
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
const triggerFingerprint = supervisorGraphFingerprint({
|
|
269
|
+
rootWorkId: plans.active_root_work_id,
|
|
270
|
+
generation: plans.generation,
|
|
271
|
+
work,
|
|
272
|
+
});
|
|
273
|
+
const latestHandledSameGraph = plans.latest?.request.trigger_fingerprint === triggerFingerprint &&
|
|
274
|
+
plans.latest.request.prior_root_work_id === plans.active_root_work_id &&
|
|
275
|
+
plans.latest.status === "attention";
|
|
276
|
+
if (latestHandledSameGraph && plans.latest) {
|
|
277
|
+
return {
|
|
278
|
+
...base,
|
|
279
|
+
state: "awaiting_attention",
|
|
280
|
+
reason: plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`,
|
|
281
|
+
next_action: "none",
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (!root && intent.mission) {
|
|
285
|
+
if (!intent.replanning || plans.plans.length >= intent.replanning.max_replans) {
|
|
286
|
+
return {
|
|
287
|
+
...base,
|
|
288
|
+
state: "budget_exhausted",
|
|
289
|
+
reason: "mission exhausted its planning budget before creating an initial milestone",
|
|
290
|
+
next_action: "none",
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
...base,
|
|
295
|
+
state: "ready",
|
|
296
|
+
reason: "mission is ready for its initial bounded milestone plan",
|
|
297
|
+
next_action: "plan_initial",
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if (!root)
|
|
301
|
+
throw new Error(`supervisor ${intent.id} root work is missing`);
|
|
302
|
+
if (root.projection.state === "succeeded" && intent.mission) {
|
|
303
|
+
if (!intent.replanning || plans.plans.length >= intent.replanning.max_replans) {
|
|
304
|
+
return {
|
|
305
|
+
...base,
|
|
306
|
+
state: "budget_exhausted",
|
|
307
|
+
reason: "mission exhausted its planning budget at a milestone boundary",
|
|
308
|
+
next_action: "none",
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
...base,
|
|
313
|
+
state: "ready",
|
|
314
|
+
reason: `milestone ${plans.milestones_completed} is accepted and requires mission reassessment`,
|
|
315
|
+
next_action: "plan_milestone",
|
|
316
|
+
};
|
|
317
|
+
}
|
|
193
318
|
if (root.projection.state === "succeeded") {
|
|
194
319
|
return {
|
|
195
320
|
...base,
|
|
@@ -268,6 +393,38 @@ function deriveProjection(coordRoot, intent, work, ignoreLease) {
|
|
|
268
393
|
next_action: "retry",
|
|
269
394
|
};
|
|
270
395
|
}
|
|
396
|
+
if (attemptsUsed >= intent.limits.max_total_attempts && cancelled.length === 0) {
|
|
397
|
+
return {
|
|
398
|
+
...base,
|
|
399
|
+
state: "budget_exhausted",
|
|
400
|
+
reason: `goal exhausted its ${intent.limits.max_total_attempts} total attempts`,
|
|
401
|
+
next_action: "none",
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
const canReplan = intent.replanning !== undefined &&
|
|
405
|
+
plans.plans.length < intent.replanning.max_replans &&
|
|
406
|
+
cancelled.length === 0 &&
|
|
407
|
+
!latestHandledSameGraph;
|
|
408
|
+
if (canReplan) {
|
|
409
|
+
return {
|
|
410
|
+
...base,
|
|
411
|
+
state: "ready",
|
|
412
|
+
reason: terminalBlocked.length
|
|
413
|
+
? `${terminalBlocked.length} terminally blocked work item${terminalBlocked.length === 1 ? " can" : "s can"} be replanned`
|
|
414
|
+
: "the active graph has no legal progress action and may be replanned",
|
|
415
|
+
next_action: "replan",
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
if (intent.replanning &&
|
|
419
|
+
plans.plans.length >= intent.replanning.max_replans &&
|
|
420
|
+
cancelled.length === 0) {
|
|
421
|
+
return {
|
|
422
|
+
...base,
|
|
423
|
+
state: "budget_exhausted",
|
|
424
|
+
reason: `goal exhausted its ${intent.replanning.max_replans} replans`,
|
|
425
|
+
next_action: "none",
|
|
426
|
+
};
|
|
427
|
+
}
|
|
271
428
|
return {
|
|
272
429
|
...base,
|
|
273
430
|
state: "blocked",
|
|
@@ -304,6 +461,19 @@ function validateIntent(intent, goalId) {
|
|
|
304
461
|
}
|
|
305
462
|
bounded(intent.title, "supervisor title", MAX_TITLE);
|
|
306
463
|
assertWorkId(intent.root_work_id);
|
|
464
|
+
if (intent.mission) {
|
|
465
|
+
const normalizedMission = normalizeMission({
|
|
466
|
+
objective: intent.mission.objective,
|
|
467
|
+
acceptance: intent.mission.acceptance,
|
|
468
|
+
maxMilestones: intent.mission.max_milestones,
|
|
469
|
+
});
|
|
470
|
+
if (JSON.stringify(normalizedMission) !== JSON.stringify(intent.mission)) {
|
|
471
|
+
throw new Error(`supervisor intent ${goalId} mission is not canonical`);
|
|
472
|
+
}
|
|
473
|
+
if (!intent.replanning) {
|
|
474
|
+
throw new Error(`supervisor intent ${goalId} mission requires replanning`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
307
477
|
const specialists = normalizeWorkflowSpecialists(intent.specialists);
|
|
308
478
|
if (JSON.stringify(specialists) !== JSON.stringify(intent.specialists)) {
|
|
309
479
|
throw new Error(`supervisor intent ${goalId} specialists are not canonical`);
|
|
@@ -314,6 +484,30 @@ function validateIntent(intent, goalId) {
|
|
|
314
484
|
if (JSON.stringify(normalizeAutomation(intent.automation)) !== JSON.stringify(intent.automation)) {
|
|
315
485
|
throw new Error(`supervisor intent ${goalId} automation policy is not canonical`);
|
|
316
486
|
}
|
|
487
|
+
if (intent.replanning)
|
|
488
|
+
validateReplanning(intent.replanning, specialists, goalId);
|
|
489
|
+
}
|
|
490
|
+
function normalizeMission(input) {
|
|
491
|
+
if (!Array.isArray(input.acceptance)) {
|
|
492
|
+
throw new Error("supervisor mission acceptance must be an array");
|
|
493
|
+
}
|
|
494
|
+
if (input.acceptance.length < 1 || input.acceptance.length > MAX_ACCEPTANCE) {
|
|
495
|
+
throw new Error(`supervisor mission acceptance must contain 1 to ${MAX_ACCEPTANCE} criteria`);
|
|
496
|
+
}
|
|
497
|
+
const acceptance = input.acceptance.map((criterion, index) => bounded(criterion, `supervisor mission acceptance[${index}]`, 500));
|
|
498
|
+
if (new Set(acceptance).size !== acceptance.length) {
|
|
499
|
+
throw new Error("supervisor mission acceptance criteria must be unique");
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
objective: bounded(input.objective, "supervisor mission objective", MAX_OBJECTIVE),
|
|
503
|
+
acceptance,
|
|
504
|
+
max_milestones: positive(input.maxMilestones ?? 4, "supervisor mission max_milestones", 20),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function missionTitle(mission) {
|
|
508
|
+
return mission.objective.length <= MAX_TITLE
|
|
509
|
+
? mission.objective
|
|
510
|
+
: `${mission.objective.slice(0, MAX_TITLE - 1).trimEnd()}…`;
|
|
317
511
|
}
|
|
318
512
|
function normalizeLimits(input) {
|
|
319
513
|
return {
|
|
@@ -332,6 +526,101 @@ function normalizeAutomation(input) {
|
|
|
332
526
|
retry_blocked: boolean(input?.retry_blocked ?? false, "retry_blocked"),
|
|
333
527
|
};
|
|
334
528
|
}
|
|
529
|
+
function normalizeReplanning(input, specialistsInput) {
|
|
530
|
+
const specialists = normalizeWorkflowSpecialists(specialistsInput);
|
|
531
|
+
const plannerSpecialist = bounded(input.plannerSpecialist, "planner specialist", 100);
|
|
532
|
+
if (!specialists[plannerSpecialist]) {
|
|
533
|
+
throw new Error(`planner specialist ${plannerSpecialist} is not present in the frozen team`);
|
|
534
|
+
}
|
|
535
|
+
const entries = Object.entries(input.templates);
|
|
536
|
+
if (entries.length < 1 || entries.length > MAX_TEMPLATES) {
|
|
537
|
+
throw new Error(`replanning templates must contain 1 to ${MAX_TEMPLATES} entries`);
|
|
538
|
+
}
|
|
539
|
+
const templates = Object.fromEntries(entries
|
|
540
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
541
|
+
.map(([id, template]) => {
|
|
542
|
+
if (!TEMPLATE_ID.test(id))
|
|
543
|
+
throw new Error(`invalid replanning template id ${id}`);
|
|
544
|
+
const path = resolve(template.workflowPath);
|
|
545
|
+
if (!existsSync(path))
|
|
546
|
+
throw new Error(`replanning template ${id} does not exist at ${path}`);
|
|
547
|
+
const maxAttempts = positive(template.maxAttempts ?? 3, `replanning template ${id} maxAttempts`, 100);
|
|
548
|
+
return [
|
|
549
|
+
id,
|
|
550
|
+
{
|
|
551
|
+
workflow: { path, sha256: workflowScriptDigest(path) },
|
|
552
|
+
max_attempts: maxAttempts,
|
|
553
|
+
root: boolean(template.root ?? false, `replanning template ${id} root`),
|
|
554
|
+
},
|
|
555
|
+
];
|
|
556
|
+
}));
|
|
557
|
+
if (!Object.values(templates).some((template) => template.root)) {
|
|
558
|
+
throw new Error("replanning requires at least one root-capable template");
|
|
559
|
+
}
|
|
560
|
+
const maxWorkItemsPerPlan = positive(input.maxWorkItemsPerPlan ?? 8, "replanning max_work_items_per_plan", 25);
|
|
561
|
+
const maxTotalWorkItems = positive(input.maxTotalWorkItems ?? 25, "replanning max_total_work_items", 100);
|
|
562
|
+
if (maxWorkItemsPerPlan > maxTotalWorkItems) {
|
|
563
|
+
throw new Error("replanning max_work_items_per_plan cannot exceed max_total_work_items");
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
planner_specialist: plannerSpecialist,
|
|
567
|
+
auto_apply: boolean(input.autoApply ?? false, "replanning auto_apply"),
|
|
568
|
+
max_replans: positive(input.maxReplans ?? 5, "replanning max_replans", 100),
|
|
569
|
+
max_work_items_per_plan: maxWorkItemsPerPlan,
|
|
570
|
+
max_total_work_items: maxTotalWorkItems,
|
|
571
|
+
templates,
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
function validateReplanning(policy, specialists, goalId) {
|
|
575
|
+
if (!specialists[policy.planner_specialist]) {
|
|
576
|
+
throw new Error(`supervisor ${goalId} planner specialist is not in the frozen team`);
|
|
577
|
+
}
|
|
578
|
+
boolean(policy.auto_apply, "replanning auto_apply");
|
|
579
|
+
positive(policy.max_replans, "replanning max_replans", 100);
|
|
580
|
+
positive(policy.max_work_items_per_plan, "replanning max_work_items_per_plan", 25);
|
|
581
|
+
positive(policy.max_total_work_items, "replanning max_total_work_items", 100);
|
|
582
|
+
if (policy.max_work_items_per_plan > policy.max_total_work_items) {
|
|
583
|
+
throw new Error(`supervisor ${goalId} replanning work-item bounds are inconsistent`);
|
|
584
|
+
}
|
|
585
|
+
const entries = Object.entries(policy.templates ?? {});
|
|
586
|
+
if (entries.length < 1 || entries.length > MAX_TEMPLATES) {
|
|
587
|
+
throw new Error(`supervisor ${goalId} has invalid replanning templates`);
|
|
588
|
+
}
|
|
589
|
+
let rootCapable = false;
|
|
590
|
+
for (const [id, template] of entries) {
|
|
591
|
+
if (!TEMPLATE_ID.test(id) ||
|
|
592
|
+
!isAbsolute(template.workflow?.path) ||
|
|
593
|
+
!/^[a-f0-9]{64}$/.test(template.workflow?.sha256) ||
|
|
594
|
+
!Number.isSafeInteger(template.max_attempts) ||
|
|
595
|
+
template.max_attempts < 1 ||
|
|
596
|
+
template.max_attempts > 100 ||
|
|
597
|
+
typeof template.root !== "boolean") {
|
|
598
|
+
throw new Error(`supervisor ${goalId} replanning template ${id} is invalid`);
|
|
599
|
+
}
|
|
600
|
+
rootCapable ||= template.root;
|
|
601
|
+
}
|
|
602
|
+
if (!rootCapable)
|
|
603
|
+
throw new Error(`supervisor ${goalId} has no root-capable replanning template`);
|
|
604
|
+
}
|
|
605
|
+
function collectGovernedWork(coordRoot, originalRootWorkId, plans, active) {
|
|
606
|
+
const governed = new Map();
|
|
607
|
+
if (workIntentExists(coordRoot, originalRootWorkId)) {
|
|
608
|
+
for (const record of collectSupervisorWork(coordRoot, originalRootWorkId)) {
|
|
609
|
+
governed.set(record.intent.id, record);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
for (const workId of plans.applied_work_ids)
|
|
613
|
+
governed.set(workId, readWorkItem(coordRoot, workId));
|
|
614
|
+
for (const record of active)
|
|
615
|
+
governed.set(record.intent.id, record);
|
|
616
|
+
return [...governed.values()];
|
|
617
|
+
}
|
|
618
|
+
function workIntentExists(coordRoot, workId) {
|
|
619
|
+
return existsSync(join(resolve(coordRoot), ".harnery", "work", workId, "intent.json"));
|
|
620
|
+
}
|
|
621
|
+
function missionRootId(goalId) {
|
|
622
|
+
return `${goalId.slice(0, 87)}-mission-root`;
|
|
623
|
+
}
|
|
335
624
|
function supervisorDir(coordRoot, goalId) {
|
|
336
625
|
assertSupervisorId(goalId);
|
|
337
626
|
return join(resolve(coordRoot), ".harnery", "supervisors", goalId);
|