harnery 0.17.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 +40 -3
- package/dist/core/supervisor/index.d.ts +2 -2
- package/dist/core/supervisor/index.d.ts.map +1 -1
- package/dist/core/supervisor/plan-read.d.ts.map +1 -1
- package/dist/core/supervisor/plan-read.js +47 -1
- package/dist/core/supervisor/plan-types.d.ts +13 -3
- package/dist/core/supervisor/plan-types.d.ts.map +1 -1
- package/dist/core/supervisor/planning.d.ts.map +1 -1
- package/dist/core/supervisor/planning.js +113 -20
- package/dist/core/supervisor/read.d.ts +2 -2
- package/dist/core/supervisor/read.d.ts.map +1 -1
- package/dist/core/supervisor/runner.d.ts.map +1 -1
- package/dist/core/supervisor/runner.js +7 -2
- package/dist/core/supervisor/service.js +3 -0
- package/dist/core/supervisor/state.d.ts +17 -2
- package/dist/core/supervisor/state.d.ts.map +1 -1
- package/dist/core/supervisor/state.js +137 -27
- package/package.json +1 -1
- package/src/commands/supervisor.ts +47 -4
- package/src/core/supervisor/index.ts +3 -0
- package/src/core/supervisor/plan-read.ts +58 -1
- package/src/core/supervisor/plan-types.ts +14 -1
- package/src/core/supervisor/planning.ts +136 -14
- package/src/core/supervisor/read.ts +2 -0
- package/src/core/supervisor/runner.ts +9 -2
- package/src/core/supervisor/service.ts +3 -0
- package/src/core/supervisor/state.ts +176 -28
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/runner.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,gBAAgB,EAItB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX,oBAAoB,GACpB,SAAS,GACT,kBAAkB,GAClB,aAAa,GACb,eAAe,CAAC;AAEpB,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACnC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,WAAW,EAAE,oBAAoB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,UAAU,EAAE,oBAAoB,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC;IACtF,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAQD,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/runner.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,gBAAgB,EAItB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX,oBAAoB,GACpB,SAAS,GACT,kBAAkB,GAClB,aAAa,GACb,eAAe,CAAC;AAEpB,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACnC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,WAAW,EAAE,oBAAoB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,UAAU,EAAE,oBAAoB,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC;IACtF,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAQD,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4L3F"}
|
|
@@ -33,8 +33,10 @@ export async function runSupervisor(input) {
|
|
|
33
33
|
}
|
|
34
34
|
const before = graphFingerprint(record);
|
|
35
35
|
let changed = false;
|
|
36
|
-
if (record.projection.next_action === "replan"
|
|
37
|
-
|
|
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}`);
|
|
38
40
|
const plan = await runSupervisorPlanner({
|
|
39
41
|
coordRoot,
|
|
40
42
|
record,
|
|
@@ -48,6 +50,9 @@ export async function runSupervisor(input) {
|
|
|
48
50
|
if (mode === "tick") {
|
|
49
51
|
return report("tick_complete", `planner produced ${plan.status} plan ${plan.plan_id}`, record);
|
|
50
52
|
}
|
|
53
|
+
if (record.projection.state === "succeeded") {
|
|
54
|
+
return report("succeeded", record.projection.reason, record);
|
|
55
|
+
}
|
|
51
56
|
if (record.projection.state !== "ready") {
|
|
52
57
|
return report(stopForProjection(record.projection), record.projection.reason, record);
|
|
53
58
|
}
|
|
@@ -434,9 +434,12 @@ function projectionFingerprint(projection) {
|
|
|
434
434
|
projection.attempts_used,
|
|
435
435
|
projection.attempts_remaining,
|
|
436
436
|
projection.root_work_id,
|
|
437
|
+
projection.root_materialized,
|
|
437
438
|
projection.plan_generation,
|
|
438
439
|
projection.replans_used,
|
|
439
440
|
projection.replans_remaining,
|
|
441
|
+
projection.milestones_completed,
|
|
442
|
+
projection.milestones_remaining,
|
|
440
443
|
projection.pending_plan_id,
|
|
441
444
|
projection.latest_plan_status,
|
|
442
445
|
projection.ready_work,
|
|
@@ -15,6 +15,16 @@ export interface SupervisorAutomationPolicy {
|
|
|
15
15
|
resume_approved: boolean;
|
|
16
16
|
retry_blocked: boolean;
|
|
17
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
|
+
}
|
|
18
28
|
export interface SupervisorIntent {
|
|
19
29
|
schema_version: typeof SUPERVISOR_INTENT_SCHEMA_VERSION;
|
|
20
30
|
id: string;
|
|
@@ -23,15 +33,17 @@ export interface SupervisorIntent {
|
|
|
23
33
|
specialists: Record<string, WorkflowSpecialistProfile>;
|
|
24
34
|
limits: SupervisorLimits;
|
|
25
35
|
automation: SupervisorAutomationPolicy;
|
|
36
|
+
mission?: SupervisorMission;
|
|
26
37
|
replanning?: SupervisorReplanningPolicy;
|
|
27
38
|
created_at: string;
|
|
28
39
|
}
|
|
29
40
|
export type SupervisorState = "ready" | "running" | "awaiting_attention" | "blocked" | "budget_exhausted" | "succeeded";
|
|
30
|
-
export type SupervisorNextAction = "run" | "wait_for_run" | "resolve_approval" | "review" | "retry" | "replan" | "review_plan" | "none";
|
|
41
|
+
export type SupervisorNextAction = "run" | "wait_for_run" | "resolve_approval" | "review" | "retry" | "replan" | "plan_initial" | "plan_milestone" | "review_plan" | "none";
|
|
31
42
|
export interface SupervisorProjection {
|
|
32
43
|
id: string;
|
|
33
44
|
title: string;
|
|
34
45
|
root_work_id: string;
|
|
46
|
+
root_materialized: boolean;
|
|
35
47
|
state: SupervisorState;
|
|
36
48
|
reason: string;
|
|
37
49
|
next_action: SupervisorNextAction;
|
|
@@ -46,6 +58,8 @@ export interface SupervisorProjection {
|
|
|
46
58
|
plan_generation: number;
|
|
47
59
|
replans_used: number;
|
|
48
60
|
replans_remaining: number;
|
|
61
|
+
milestones_completed: number;
|
|
62
|
+
milestones_remaining: number;
|
|
49
63
|
pending_plan_id?: string;
|
|
50
64
|
latest_plan_status?: SupervisorPlanRecord["status"];
|
|
51
65
|
governed_work_ids: string[];
|
|
@@ -58,12 +72,13 @@ export interface SupervisorRecord {
|
|
|
58
72
|
}
|
|
59
73
|
export interface CreateSupervisorInput {
|
|
60
74
|
coordRoot: string;
|
|
61
|
-
rootWorkId
|
|
75
|
+
rootWorkId?: string;
|
|
62
76
|
specialists: Readonly<Record<string, WorkflowSpecialistProfile>>;
|
|
63
77
|
title?: string;
|
|
64
78
|
id?: string;
|
|
65
79
|
limits?: Partial<SupervisorLimits>;
|
|
66
80
|
automation?: Partial<SupervisorAutomationPolicy>;
|
|
81
|
+
mission?: CreateSupervisorMissionInput;
|
|
67
82
|
replanning?: CreateSupervisorReplanningInput;
|
|
68
83
|
}
|
|
69
84
|
export declare function createSupervisor(input: CreateSupervisorInput): 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;AAG9E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,OAAO,EACL,KAAK,+BAA+B,EAEpC,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAEhC,MAAM,iBAAiB,CAAC;AAEzB,eAAO,MAAM,gCAAgC,EAAG,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,27 +10,44 @@ import { supervisorGraphFingerprint, } from "./plan-types.js";
|
|
|
10
10
|
export const SUPERVISOR_INTENT_SCHEMA_VERSION = 1;
|
|
11
11
|
const GOAL_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
12
12
|
const MAX_TITLE = 200;
|
|
13
|
+
const MAX_OBJECTIVE = 4_000;
|
|
14
|
+
const MAX_ACCEPTANCE = 50;
|
|
13
15
|
const MAX_INTENT_BYTES = 256 * 1024;
|
|
14
16
|
const MAX_TEMPLATES = 20;
|
|
15
17
|
const TEMPLATE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
16
18
|
const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
17
19
|
export function createSupervisor(input) {
|
|
18
20
|
const coordRoot = resolve(input.coordRoot);
|
|
19
|
-
assertWorkId(input.rootWorkId);
|
|
20
|
-
const root = readWorkItem(coordRoot, input.rootWorkId);
|
|
21
21
|
const id = input.id ?? newSupervisorId();
|
|
22
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;
|
|
23
33
|
const replanning = input.replanning
|
|
24
34
|
? normalizeReplanning(input.replanning, input.specialists)
|
|
25
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
|
+
}
|
|
26
42
|
const intent = {
|
|
27
43
|
schema_version: SUPERVISOR_INTENT_SCHEMA_VERSION,
|
|
28
44
|
id,
|
|
29
|
-
title: bounded(input.title ?? root
|
|
30
|
-
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),
|
|
31
47
|
specialists: normalizeWorkflowSpecialists(input.specialists),
|
|
32
48
|
limits: normalizeLimits(input.limits),
|
|
33
49
|
automation: normalizeAutomation(input.automation),
|
|
50
|
+
...(mission ? { mission } : {}),
|
|
34
51
|
...(replanning ? { replanning } : {}),
|
|
35
52
|
created_at: new Date().toISOString(),
|
|
36
53
|
};
|
|
@@ -136,7 +153,11 @@ function readSupervisorInternal(coordRootRaw, goalId, ignoreLease) {
|
|
|
136
153
|
const coordRoot = resolve(coordRootRaw);
|
|
137
154
|
const intent = readSupervisorIntent(coordRoot, goalId);
|
|
138
155
|
const plans = readSupervisorPlans(coordRoot, intent.id, intent.root_work_id);
|
|
139
|
-
const
|
|
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) : [];
|
|
140
161
|
const governed = collectGovernedWork(coordRoot, intent.root_work_id, plans, work);
|
|
141
162
|
return {
|
|
142
163
|
intent,
|
|
@@ -147,8 +168,6 @@ function readSupervisorInternal(coordRootRaw, goalId, ignoreLease) {
|
|
|
147
168
|
}
|
|
148
169
|
function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease) {
|
|
149
170
|
const root = work.find((record) => record.intent.id === plans.active_root_work_id);
|
|
150
|
-
if (!root)
|
|
151
|
-
throw new Error(`supervisor ${intent.id} root work is missing`);
|
|
152
171
|
const attemptsUsed = governed.reduce((sum, record) => sum + record.projection.attempts_used, 0);
|
|
153
172
|
const readyWork = work
|
|
154
173
|
.filter((record) => record.projection.state === "ready")
|
|
@@ -181,10 +200,14 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
181
200
|
...cancelled,
|
|
182
201
|
...terminalBlocked,
|
|
183
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);
|
|
184
206
|
const base = {
|
|
185
207
|
id: intent.id,
|
|
186
208
|
title: intent.title,
|
|
187
209
|
root_work_id: plans.active_root_work_id,
|
|
210
|
+
root_materialized: root !== undefined,
|
|
188
211
|
work_ids: work.map((record) => record.intent.id),
|
|
189
212
|
ready_work: readyWork,
|
|
190
213
|
resumable_work: resumableWork,
|
|
@@ -196,6 +219,8 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
196
219
|
plan_generation: plans.generation,
|
|
197
220
|
replans_used: plans.plans.length,
|
|
198
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),
|
|
199
224
|
pending_plan_id: plans.latest?.status === "proposed" ? plans.latest.request.id : undefined,
|
|
200
225
|
latest_plan_status: plans.latest?.status,
|
|
201
226
|
governed_work_ids: governed.map((record) => record.intent.id),
|
|
@@ -208,11 +233,11 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
208
233
|
next_action: "wait_for_run",
|
|
209
234
|
};
|
|
210
235
|
}
|
|
211
|
-
if (
|
|
236
|
+
if (intent.mission && plans.completed) {
|
|
212
237
|
return {
|
|
213
238
|
...base,
|
|
214
239
|
state: "succeeded",
|
|
215
|
-
reason: "
|
|
240
|
+
reason: "mission completion was explicitly accepted",
|
|
216
241
|
next_action: "none",
|
|
217
242
|
};
|
|
218
243
|
}
|
|
@@ -240,6 +265,64 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
240
265
|
next_action: "replan",
|
|
241
266
|
};
|
|
242
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
|
+
}
|
|
318
|
+
if (root.projection.state === "succeeded") {
|
|
319
|
+
return {
|
|
320
|
+
...base,
|
|
321
|
+
state: "succeeded",
|
|
322
|
+
reason: "root work was explicitly accepted",
|
|
323
|
+
next_action: "none",
|
|
324
|
+
};
|
|
325
|
+
}
|
|
243
326
|
const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
|
|
244
327
|
const attemptDispatchable = [
|
|
245
328
|
...readyWork,
|
|
@@ -318,14 +401,6 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
318
401
|
next_action: "none",
|
|
319
402
|
};
|
|
320
403
|
}
|
|
321
|
-
const triggerFingerprint = supervisorGraphFingerprint({
|
|
322
|
-
rootWorkId: plans.active_root_work_id,
|
|
323
|
-
generation: plans.generation,
|
|
324
|
-
work,
|
|
325
|
-
});
|
|
326
|
-
const latestHandledSameGraph = plans.latest?.request.trigger_fingerprint === triggerFingerprint &&
|
|
327
|
-
plans.latest.request.prior_root_work_id === plans.active_root_work_id &&
|
|
328
|
-
plans.latest.status === "attention";
|
|
329
404
|
const canReplan = intent.replanning !== undefined &&
|
|
330
405
|
plans.plans.length < intent.replanning.max_replans &&
|
|
331
406
|
cancelled.length === 0 &&
|
|
@@ -350,14 +425,6 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
350
425
|
next_action: "none",
|
|
351
426
|
};
|
|
352
427
|
}
|
|
353
|
-
if (latestHandledSameGraph && plans.latest) {
|
|
354
|
-
return {
|
|
355
|
-
...base,
|
|
356
|
-
state: "awaiting_attention",
|
|
357
|
-
reason: plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`,
|
|
358
|
-
next_action: "none",
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
428
|
return {
|
|
362
429
|
...base,
|
|
363
430
|
state: "blocked",
|
|
@@ -394,6 +461,19 @@ function validateIntent(intent, goalId) {
|
|
|
394
461
|
}
|
|
395
462
|
bounded(intent.title, "supervisor title", MAX_TITLE);
|
|
396
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
|
+
}
|
|
397
477
|
const specialists = normalizeWorkflowSpecialists(intent.specialists);
|
|
398
478
|
if (JSON.stringify(specialists) !== JSON.stringify(intent.specialists)) {
|
|
399
479
|
throw new Error(`supervisor intent ${goalId} specialists are not canonical`);
|
|
@@ -407,6 +487,28 @@ function validateIntent(intent, goalId) {
|
|
|
407
487
|
if (intent.replanning)
|
|
408
488
|
validateReplanning(intent.replanning, specialists, goalId);
|
|
409
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()}…`;
|
|
511
|
+
}
|
|
410
512
|
function normalizeLimits(input) {
|
|
411
513
|
return {
|
|
412
514
|
max_cycles: positive(input?.max_cycles ?? 50, "supervisor max_cycles", 1_000),
|
|
@@ -502,8 +604,10 @@ function validateReplanning(policy, specialists, goalId) {
|
|
|
502
604
|
}
|
|
503
605
|
function collectGovernedWork(coordRoot, originalRootWorkId, plans, active) {
|
|
504
606
|
const governed = new Map();
|
|
505
|
-
|
|
506
|
-
|
|
607
|
+
if (workIntentExists(coordRoot, originalRootWorkId)) {
|
|
608
|
+
for (const record of collectSupervisorWork(coordRoot, originalRootWorkId)) {
|
|
609
|
+
governed.set(record.intent.id, record);
|
|
610
|
+
}
|
|
507
611
|
}
|
|
508
612
|
for (const workId of plans.applied_work_ids)
|
|
509
613
|
governed.set(workId, readWorkItem(coordRoot, workId));
|
|
@@ -511,6 +615,12 @@ function collectGovernedWork(coordRoot, originalRootWorkId, plans, active) {
|
|
|
511
615
|
governed.set(record.intent.id, record);
|
|
512
616
|
return [...governed.values()];
|
|
513
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
|
+
}
|
|
514
624
|
function supervisorDir(coordRoot, goalId) {
|
|
515
625
|
assertSupervisorId(goalId);
|
|
516
626
|
return join(resolve(coordRoot), ".harnery", "supervisors", goalId);
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import type { PolicyIsolation } from "../core/policy/index.ts";
|
|
|
9
9
|
import { loadPolicyFile } from "../core/policy/index.ts";
|
|
10
10
|
import {
|
|
11
11
|
approveSupervisorPlan,
|
|
12
|
+
type CreateSupervisorMissionInput,
|
|
12
13
|
type CreateSupervisorReplanningInput,
|
|
13
14
|
configureSupervisorService,
|
|
14
15
|
createSupervisor,
|
|
@@ -46,6 +47,7 @@ interface CreateOpts {
|
|
|
46
47
|
resumeApproved?: boolean;
|
|
47
48
|
retryBlocked?: boolean;
|
|
48
49
|
replanning?: string;
|
|
50
|
+
mission?: string;
|
|
49
51
|
json?: boolean;
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -75,8 +77,8 @@ export function registerSupervisorCommand(program: Command, emit: EmitContext):
|
|
|
75
77
|
.description("Run a bounded specialist team over a durable-work dependency graph.");
|
|
76
78
|
|
|
77
79
|
supervisor
|
|
78
|
-
.command("create
|
|
79
|
-
.description("Freeze
|
|
80
|
+
.command("create [root-work-id]")
|
|
81
|
+
.description("Freeze an existing-root goal or an objective-first bounded mission.")
|
|
80
82
|
.requiredOption("--team <file>", "JSON object mapping specialist ids to profiles")
|
|
81
83
|
.option("--id <goal-id>", "Stable goal id (generated by default)")
|
|
82
84
|
.option("--title <text>", "Goal title (defaults to root work title)")
|
|
@@ -90,8 +92,9 @@ export function registerSupervisorCommand(program: Command, emit: EmitContext):
|
|
|
90
92
|
.option("--no-resume-approved", "Stop after an approval instead of resuming its parked run")
|
|
91
93
|
.option("--retry-blocked", "Allow bounded retry of blocked work")
|
|
92
94
|
.option("--replanning <file>", "Frozen planner policy and allowed workflow-template catalog")
|
|
95
|
+
.option("--mission <file>", "Frozen objective, acceptance criteria, and milestone bound")
|
|
93
96
|
.option("--json", "Emit the complete supervisor record as JSON")
|
|
94
|
-
.action((rootWorkId: string, opts: CreateOpts) => {
|
|
97
|
+
.action((rootWorkId: string | undefined, opts: CreateOpts) => {
|
|
95
98
|
withSupervisorRoot(emit, (coordRoot) => {
|
|
96
99
|
const specialists = readTeamFile(opts.team);
|
|
97
100
|
for (const [id, profile] of Object.entries(specialists)) {
|
|
@@ -121,6 +124,7 @@ export function registerSupervisorCommand(program: Command, emit: EmitContext):
|
|
|
121
124
|
resume_approved: opts.resumeApproved,
|
|
122
125
|
retry_blocked: opts.retryBlocked,
|
|
123
126
|
},
|
|
127
|
+
mission: opts.mission ? readMissionFile(opts.mission) : undefined,
|
|
124
128
|
replanning: opts.replanning ? readReplanningFile(opts.replanning) : undefined,
|
|
125
129
|
}),
|
|
126
130
|
opts.json,
|
|
@@ -626,6 +630,34 @@ function readReplanningFile(path: string): CreateSupervisorReplanningInput {
|
|
|
626
630
|
};
|
|
627
631
|
}
|
|
628
632
|
|
|
633
|
+
function readMissionFile(path: string): CreateSupervisorMissionInput {
|
|
634
|
+
const absolute = resolve(path);
|
|
635
|
+
let value: unknown;
|
|
636
|
+
try {
|
|
637
|
+
value = JSON.parse(readFileSync(absolute, "utf8"));
|
|
638
|
+
} catch (error) {
|
|
639
|
+
throw new Error(`cannot read supervisor mission at ${absolute}: ${(error as Error).message}`);
|
|
640
|
+
}
|
|
641
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
642
|
+
throw new Error("supervisor mission must be a JSON object");
|
|
643
|
+
}
|
|
644
|
+
const config = value as Record<string, unknown>;
|
|
645
|
+
if (typeof config.objective !== "string") {
|
|
646
|
+
throw new Error("supervisor mission objective must be a string");
|
|
647
|
+
}
|
|
648
|
+
if (
|
|
649
|
+
!Array.isArray(config.acceptance) ||
|
|
650
|
+
config.acceptance.some((item) => typeof item !== "string")
|
|
651
|
+
) {
|
|
652
|
+
throw new Error("supervisor mission acceptance must be an array of strings");
|
|
653
|
+
}
|
|
654
|
+
return {
|
|
655
|
+
objective: config.objective,
|
|
656
|
+
acceptance: config.acceptance as string[],
|
|
657
|
+
maxMilestones: jsonInteger(config.max_milestones, "mission max_milestones"),
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
629
661
|
function emitSupervisor(
|
|
630
662
|
record: SupervisorRecord,
|
|
631
663
|
json: boolean | undefined,
|
|
@@ -645,7 +677,7 @@ function emitSupervisor(
|
|
|
645
677
|
const lines = [
|
|
646
678
|
`${record.intent.id}: ${record.intent.title}`,
|
|
647
679
|
`state: ${projection.state}`,
|
|
648
|
-
`root: ${projection.root_work_id}`,
|
|
680
|
+
`root: ${projection.root_materialized ? projection.root_work_id : "pending initial plan"}`,
|
|
649
681
|
`reason: ${projection.reason}`,
|
|
650
682
|
`next: ${projection.next_action}`,
|
|
651
683
|
`work: ${projection.work_ids.length}`,
|
|
@@ -663,6 +695,13 @@ function emitSupervisor(
|
|
|
663
695
|
}
|
|
664
696
|
if (projection.pending_plan_id) lines.push(`pending plan: ${projection.pending_plan_id}`);
|
|
665
697
|
}
|
|
698
|
+
if (record.intent.mission) {
|
|
699
|
+
lines.push(`mission: ${record.intent.mission.objective}`);
|
|
700
|
+
lines.push(
|
|
701
|
+
`milestones: ${projection.milestones_completed}/${record.intent.mission.max_milestones}`,
|
|
702
|
+
);
|
|
703
|
+
lines.push(`mission acceptance: ${record.intent.mission.acceptance.length}`);
|
|
704
|
+
}
|
|
666
705
|
if (projection.attention_work.length) {
|
|
667
706
|
lines.push(`attention: ${projection.attention_work.join(", ")}`);
|
|
668
707
|
}
|
|
@@ -724,6 +763,7 @@ function emitPlan(plan: SupervisorPlanRecord, json: boolean | undefined, emit: E
|
|
|
724
763
|
`${plan.request.id}: ${plan.status}`,
|
|
725
764
|
`goal: ${plan.request.goal_id}`,
|
|
726
765
|
`sequence: ${plan.request.sequence}`,
|
|
766
|
+
`trigger: ${plan.request.trigger ?? "recovery"}`,
|
|
727
767
|
`prior root: ${plan.request.prior_root_work_id}`,
|
|
728
768
|
`planner run: ${plan.request.workflow_run_id}`,
|
|
729
769
|
];
|
|
@@ -731,6 +771,9 @@ function emitPlan(plan: SupervisorPlanRecord, json: boolean | undefined, emit: E
|
|
|
731
771
|
lines.push(`decision: ${plan.proposal.decision}`);
|
|
732
772
|
lines.push(`rationale: ${plan.proposal.rationale}`);
|
|
733
773
|
lines.push(`proposed work: ${plan.proposal.work.length}`);
|
|
774
|
+
if (plan.proposal.milestone) {
|
|
775
|
+
lines.push(`milestone: ${plan.proposal.milestone.sequence} ${plan.proposal.milestone.title}`);
|
|
776
|
+
}
|
|
734
777
|
}
|
|
735
778
|
if (plan.root_work_id) lines.push(`applied root: ${plan.root_work_id}`);
|
|
736
779
|
if (plan.approval_id) lines.push(`approval: ${plan.approval_id}`);
|
|
@@ -7,6 +7,7 @@ export {
|
|
|
7
7
|
type SupervisorPlanEvent,
|
|
8
8
|
type SupervisorPlanEventType,
|
|
9
9
|
type SupervisorPlanHistory,
|
|
10
|
+
type SupervisorPlanMilestone,
|
|
10
11
|
type SupervisorPlanOutcome,
|
|
11
12
|
type SupervisorPlanProposal,
|
|
12
13
|
type SupervisorPlanRecord,
|
|
@@ -53,6 +54,7 @@ export {
|
|
|
53
54
|
export {
|
|
54
55
|
assertSupervisorId,
|
|
55
56
|
type CreateSupervisorInput,
|
|
57
|
+
type CreateSupervisorMissionInput,
|
|
56
58
|
collectSupervisorWork,
|
|
57
59
|
createSupervisor,
|
|
58
60
|
listSupervisors,
|
|
@@ -62,6 +64,7 @@ export {
|
|
|
62
64
|
type SupervisorAutomationPolicy,
|
|
63
65
|
type SupervisorIntent,
|
|
64
66
|
type SupervisorLimits,
|
|
67
|
+
type SupervisorMission,
|
|
65
68
|
type SupervisorNextAction,
|
|
66
69
|
type SupervisorProjection,
|
|
67
70
|
type SupervisorRecord,
|