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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/supervisor.d.ts.map +1 -1
  3. package/dist/commands/supervisor.js +223 -5
  4. package/dist/core/supervisor/index.d.ts +4 -1
  5. package/dist/core/supervisor/index.d.ts.map +1 -1
  6. package/dist/core/supervisor/index.js +3 -0
  7. package/dist/core/supervisor/plan-api.d.ts +16 -0
  8. package/dist/core/supervisor/plan-api.d.ts.map +1 -0
  9. package/dist/core/supervisor/plan-api.js +34 -0
  10. package/dist/core/supervisor/plan-read.d.ts +4 -0
  11. package/dist/core/supervisor/plan-read.d.ts.map +1 -0
  12. package/dist/core/supervisor/plan-read.js +315 -0
  13. package/dist/core/supervisor/plan-types.d.ts +123 -0
  14. package/dist/core/supervisor/plan-types.d.ts.map +1 -0
  15. package/dist/core/supervisor/plan-types.js +14 -0
  16. package/dist/core/supervisor/planning.d.ts +26 -0
  17. package/dist/core/supervisor/planning.d.ts.map +1 -0
  18. package/dist/core/supervisor/planning.js +579 -0
  19. package/dist/core/supervisor/read.d.ts +3 -1
  20. package/dist/core/supervisor/read.d.ts.map +1 -1
  21. package/dist/core/supervisor/read.js +2 -0
  22. package/dist/core/supervisor/runner.d.ts +3 -0
  23. package/dist/core/supervisor/runner.d.ts.map +1 -1
  24. package/dist/core/supervisor/runner.js +42 -7
  25. package/dist/core/supervisor/service.d.ts.map +1 -1
  26. package/dist/core/supervisor/service.js +10 -0
  27. package/dist/core/supervisor/state.d.ts +27 -2
  28. package/dist/core/supervisor/state.d.ts.map +1 -1
  29. package/dist/core/supervisor/state.js +302 -13
  30. package/package.json +1 -1
  31. package/src/commands/supervisor.ts +275 -5
  32. package/src/core/supervisor/index.ts +21 -0
  33. package/src/core/supervisor/plan-api.ts +47 -0
  34. package/src/core/supervisor/plan-read.ts +356 -0
  35. package/src/core/supervisor/plan-types.ts +157 -0
  36. package/src/core/supervisor/planning.ts +698 -0
  37. package/src/core/supervisor/read.ts +17 -0
  38. package/src/core/supervisor/runner.ts +46 -2
  39. package/src/core/supervisor/service.ts +10 -0
  40. 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(record.work.map((work) => [
181
- work.intent.id,
182
- work.projection.state,
183
- work.projection.next_action,
184
- work.projection.attempts_used,
185
- work.events.length,
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":"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"}
@@ -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: string;
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":"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"}
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.intent.title, "supervisor title", MAX_TITLE),
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 work = collectSupervisorWork(coordRoot, intent.root_work_id);
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
- projection: deriveProjection(coordRoot, intent, work, ignoreLease),
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 === intent.root_work_id);
138
- if (!root)
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: intent.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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harnery",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Multi-agent coordination for AI coding agents - Claude Code, Cursor, and Codex.",
5
5
  "license": "MIT",
6
6
  "author": "Ryan Kelly",