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,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
2
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
3
|
import type { Command } from "commander";
|
|
4
4
|
import type { EmitContext } from "../commander.ts";
|
|
5
5
|
import { workflowSubscriptionOnly } from "../core/config.ts";
|
|
@@ -8,15 +8,22 @@ import { findCoordRoot } from "../core/hooks/resolve/coord-root.ts";
|
|
|
8
8
|
import type { PolicyIsolation } from "../core/policy/index.ts";
|
|
9
9
|
import { loadPolicyFile } from "../core/policy/index.ts";
|
|
10
10
|
import {
|
|
11
|
+
approveSupervisorPlan,
|
|
12
|
+
type CreateSupervisorMissionInput,
|
|
13
|
+
type CreateSupervisorReplanningInput,
|
|
11
14
|
configureSupervisorService,
|
|
12
15
|
createSupervisor,
|
|
13
16
|
listSupervisors,
|
|
14
17
|
readSupervisor,
|
|
18
|
+
readSupervisorPlan,
|
|
15
19
|
readSupervisorServiceConfig,
|
|
16
20
|
readSupervisorServiceStatus,
|
|
21
|
+
rejectSupervisorPlan,
|
|
17
22
|
requestSupervisorServiceStop,
|
|
18
23
|
runSupervisor,
|
|
19
24
|
runSupervisorServiceDaemon,
|
|
25
|
+
type SupervisorPlanOutcome,
|
|
26
|
+
type SupervisorPlanRecord,
|
|
20
27
|
type SupervisorRecord,
|
|
21
28
|
type SupervisorRunReport,
|
|
22
29
|
type SupervisorServiceConfig,
|
|
@@ -39,6 +46,8 @@ interface CreateOpts {
|
|
|
39
46
|
acceptPassingProof?: boolean;
|
|
40
47
|
resumeApproved?: boolean;
|
|
41
48
|
retryBlocked?: boolean;
|
|
49
|
+
replanning?: string;
|
|
50
|
+
mission?: string;
|
|
42
51
|
json?: boolean;
|
|
43
52
|
}
|
|
44
53
|
|
|
@@ -68,8 +77,8 @@ export function registerSupervisorCommand(program: Command, emit: EmitContext):
|
|
|
68
77
|
.description("Run a bounded specialist team over a durable-work dependency graph.");
|
|
69
78
|
|
|
70
79
|
supervisor
|
|
71
|
-
.command("create
|
|
72
|
-
.description("Freeze
|
|
80
|
+
.command("create [root-work-id]")
|
|
81
|
+
.description("Freeze an existing-root goal or an objective-first bounded mission.")
|
|
73
82
|
.requiredOption("--team <file>", "JSON object mapping specialist ids to profiles")
|
|
74
83
|
.option("--id <goal-id>", "Stable goal id (generated by default)")
|
|
75
84
|
.option("--title <text>", "Goal title (defaults to root work title)")
|
|
@@ -82,8 +91,10 @@ export function registerSupervisorCommand(program: Command, emit: EmitContext):
|
|
|
82
91
|
.option("--accept-passing-proof", "Allow the supervisor to explicitly accept passing proof")
|
|
83
92
|
.option("--no-resume-approved", "Stop after an approval instead of resuming its parked run")
|
|
84
93
|
.option("--retry-blocked", "Allow bounded retry of blocked work")
|
|
94
|
+
.option("--replanning <file>", "Frozen planner policy and allowed workflow-template catalog")
|
|
95
|
+
.option("--mission <file>", "Frozen objective, acceptance criteria, and milestone bound")
|
|
85
96
|
.option("--json", "Emit the complete supervisor record as JSON")
|
|
86
|
-
.action((rootWorkId: string, opts: CreateOpts) => {
|
|
97
|
+
.action((rootWorkId: string | undefined, opts: CreateOpts) => {
|
|
87
98
|
withSupervisorRoot(emit, (coordRoot) => {
|
|
88
99
|
const specialists = readTeamFile(opts.team);
|
|
89
100
|
for (const [id, profile] of Object.entries(specialists)) {
|
|
@@ -113,6 +124,8 @@ export function registerSupervisorCommand(program: Command, emit: EmitContext):
|
|
|
113
124
|
resume_approved: opts.resumeApproved,
|
|
114
125
|
retry_blocked: opts.retryBlocked,
|
|
115
126
|
},
|
|
127
|
+
mission: opts.mission ? readMissionFile(opts.mission) : undefined,
|
|
128
|
+
replanning: opts.replanning ? readReplanningFile(opts.replanning) : undefined,
|
|
116
129
|
}),
|
|
117
130
|
opts.json,
|
|
118
131
|
emit,
|
|
@@ -149,11 +162,102 @@ export function registerSupervisorCommand(program: Command, emit: EmitContext):
|
|
|
149
162
|
});
|
|
150
163
|
});
|
|
151
164
|
|
|
165
|
+
registerPlanCommand(supervisor, emit);
|
|
152
166
|
registerServiceCommand(supervisor, registry, emit);
|
|
153
167
|
registerRunCommand(supervisor, "tick", registry, emit);
|
|
154
168
|
registerRunCommand(supervisor, "run", registry, emit);
|
|
155
169
|
}
|
|
156
170
|
|
|
171
|
+
function registerPlanCommand(supervisor: Command, emit: EmitContext): void {
|
|
172
|
+
const plan = supervisor
|
|
173
|
+
.command("plan")
|
|
174
|
+
.description("Inspect and resolve bounded planner proposals for a durable goal.");
|
|
175
|
+
|
|
176
|
+
plan
|
|
177
|
+
.command("list <goal-id>")
|
|
178
|
+
.description("List append-only replanning attempts for one goal.")
|
|
179
|
+
.option("--json", "Emit complete plan records as JSON")
|
|
180
|
+
.action((goalId: string, opts: { json?: boolean }) => {
|
|
181
|
+
withSupervisorRoot(emit, (coordRoot) => {
|
|
182
|
+
const plans = readSupervisor(coordRoot, goalId).plans;
|
|
183
|
+
if (opts.json) {
|
|
184
|
+
emit.config({ format: "json" });
|
|
185
|
+
emit.data(plans);
|
|
186
|
+
} else if (plans.length === 0) {
|
|
187
|
+
emit.text(`supervisor ${goalId} has no replanning attempts\n`);
|
|
188
|
+
} else {
|
|
189
|
+
emit.text(`${plans.map(renderPlanRow).join("\n")}\n`);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
plan
|
|
195
|
+
.command("show <goal-id> <plan-id>")
|
|
196
|
+
.description("Show one planner request, schema-gated proposal, and audit events.")
|
|
197
|
+
.option("--json", "Emit the complete plan record as JSON")
|
|
198
|
+
.action((goalId: string, planId: string, opts: { json?: boolean }) => {
|
|
199
|
+
withSupervisorRoot(emit, (coordRoot) => {
|
|
200
|
+
emitPlan(readSupervisorPlan(coordRoot, goalId, planId), opts.json, emit);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
plan
|
|
205
|
+
.command("approve <goal-id> <plan-id>")
|
|
206
|
+
.description("Materialize a reviewed proposal and advance the active immutable root.")
|
|
207
|
+
.option("--actor <name>", "Actor recorded on the plan decision")
|
|
208
|
+
.option("--reason <text>", "Reason for approval")
|
|
209
|
+
.option("--json", "Emit the plan outcome as JSON")
|
|
210
|
+
.action(
|
|
211
|
+
(
|
|
212
|
+
goalId: string,
|
|
213
|
+
planId: string,
|
|
214
|
+
opts: { actor?: string; reason?: string; json?: boolean },
|
|
215
|
+
) => {
|
|
216
|
+
withSupervisorRoot(emit, (coordRoot) => {
|
|
217
|
+
emitPlanOutcome(
|
|
218
|
+
approveSupervisorPlan({
|
|
219
|
+
coordRoot,
|
|
220
|
+
goalId,
|
|
221
|
+
planId,
|
|
222
|
+
actor: opts.actor,
|
|
223
|
+
reason: opts.reason,
|
|
224
|
+
}),
|
|
225
|
+
opts.json,
|
|
226
|
+
emit,
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
},
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
plan
|
|
233
|
+
.command("reject <goal-id> <plan-id>")
|
|
234
|
+
.description("Reject a pending proposal without mutating durable work.")
|
|
235
|
+
.requiredOption("--reason <text>", "Reason the proposal is rejected")
|
|
236
|
+
.option("--actor <name>", "Actor recorded on the plan decision")
|
|
237
|
+
.option("--json", "Emit the plan outcome as JSON")
|
|
238
|
+
.action(
|
|
239
|
+
(
|
|
240
|
+
goalId: string,
|
|
241
|
+
planId: string,
|
|
242
|
+
opts: { actor?: string; reason: string; json?: boolean },
|
|
243
|
+
) => {
|
|
244
|
+
withSupervisorRoot(emit, (coordRoot) => {
|
|
245
|
+
emitPlanOutcome(
|
|
246
|
+
rejectSupervisorPlan({
|
|
247
|
+
coordRoot,
|
|
248
|
+
goalId,
|
|
249
|
+
planId,
|
|
250
|
+
actor: opts.actor,
|
|
251
|
+
reason: opts.reason,
|
|
252
|
+
}),
|
|
253
|
+
opts.json,
|
|
254
|
+
emit,
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
157
261
|
function registerServiceCommand(
|
|
158
262
|
supervisor: Command,
|
|
159
263
|
registry: ReturnType<typeof createBuiltinHarnessRegistry>,
|
|
@@ -467,6 +571,93 @@ function readTeamFile(path: string): Record<string, WorkflowSpecialistProfile> {
|
|
|
467
571
|
return value as Record<string, WorkflowSpecialistProfile>;
|
|
468
572
|
}
|
|
469
573
|
|
|
574
|
+
function readReplanningFile(path: string): CreateSupervisorReplanningInput {
|
|
575
|
+
const absolute = resolve(path);
|
|
576
|
+
let value: unknown;
|
|
577
|
+
try {
|
|
578
|
+
value = JSON.parse(readFileSync(absolute, "utf8"));
|
|
579
|
+
} catch (error) {
|
|
580
|
+
throw new Error(
|
|
581
|
+
`cannot read supervisor replanning policy at ${absolute}: ${(error as Error).message}`,
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
585
|
+
throw new Error("supervisor replanning policy must be a JSON object");
|
|
586
|
+
}
|
|
587
|
+
const config = value as Record<string, unknown>;
|
|
588
|
+
if (typeof config.planner_specialist !== "string") {
|
|
589
|
+
throw new Error("supervisor replanning planner_specialist must be a string");
|
|
590
|
+
}
|
|
591
|
+
if (
|
|
592
|
+
!config.templates ||
|
|
593
|
+
typeof config.templates !== "object" ||
|
|
594
|
+
Array.isArray(config.templates)
|
|
595
|
+
) {
|
|
596
|
+
throw new Error("supervisor replanning templates must be an object");
|
|
597
|
+
}
|
|
598
|
+
const base = dirname(absolute);
|
|
599
|
+
const templates = Object.fromEntries(
|
|
600
|
+
Object.entries(config.templates as Record<string, unknown>).map(([id, raw]) => {
|
|
601
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
602
|
+
throw new Error(`supervisor replanning template ${id} must be an object`);
|
|
603
|
+
}
|
|
604
|
+
const template = raw as Record<string, unknown>;
|
|
605
|
+
if (typeof template.workflow !== "string") {
|
|
606
|
+
throw new Error(`supervisor replanning template ${id} workflow must be a string`);
|
|
607
|
+
}
|
|
608
|
+
return [
|
|
609
|
+
id,
|
|
610
|
+
{
|
|
611
|
+
workflowPath: isAbsolute(template.workflow)
|
|
612
|
+
? template.workflow
|
|
613
|
+
: resolve(base, template.workflow),
|
|
614
|
+
maxAttempts: jsonInteger(template.max_attempts, `template ${id} max_attempts`),
|
|
615
|
+
root: jsonBoolean(template.root, `template ${id} root`),
|
|
616
|
+
},
|
|
617
|
+
];
|
|
618
|
+
}),
|
|
619
|
+
);
|
|
620
|
+
return {
|
|
621
|
+
plannerSpecialist: config.planner_specialist,
|
|
622
|
+
autoApply: jsonBoolean(config.auto_apply, "replanning auto_apply"),
|
|
623
|
+
maxReplans: jsonInteger(config.max_replans, "replanning max_replans"),
|
|
624
|
+
maxWorkItemsPerPlan: jsonInteger(
|
|
625
|
+
config.max_work_items_per_plan,
|
|
626
|
+
"replanning max_work_items_per_plan",
|
|
627
|
+
),
|
|
628
|
+
maxTotalWorkItems: jsonInteger(config.max_total_work_items, "replanning max_total_work_items"),
|
|
629
|
+
templates,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
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
|
+
|
|
470
661
|
function emitSupervisor(
|
|
471
662
|
record: SupervisorRecord,
|
|
472
663
|
json: boolean | undefined,
|
|
@@ -486,7 +677,7 @@ function emitSupervisor(
|
|
|
486
677
|
const lines = [
|
|
487
678
|
`${record.intent.id}: ${record.intent.title}`,
|
|
488
679
|
`state: ${projection.state}`,
|
|
489
|
-
`root: ${
|
|
680
|
+
`root: ${projection.root_materialized ? projection.root_work_id : "pending initial plan"}`,
|
|
490
681
|
`reason: ${projection.reason}`,
|
|
491
682
|
`next: ${projection.next_action}`,
|
|
492
683
|
`work: ${projection.work_ids.length}`,
|
|
@@ -495,6 +686,22 @@ function emitSupervisor(
|
|
|
495
686
|
`automation: accept=${record.intent.automation.accept_passing_proof}, ` +
|
|
496
687
|
`resume=${record.intent.automation.resume_approved}, retry=${record.intent.automation.retry_blocked}`,
|
|
497
688
|
];
|
|
689
|
+
if (record.intent.replanning) {
|
|
690
|
+
lines.push(
|
|
691
|
+
`replanning: generation=${projection.plan_generation}, used=${projection.replans_used}/${record.intent.replanning.max_replans}, auto_apply=${record.intent.replanning.auto_apply}`,
|
|
692
|
+
);
|
|
693
|
+
if (projection.root_work_id !== record.intent.root_work_id) {
|
|
694
|
+
lines.push(`original root: ${record.intent.root_work_id}`);
|
|
695
|
+
}
|
|
696
|
+
if (projection.pending_plan_id) lines.push(`pending plan: ${projection.pending_plan_id}`);
|
|
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
|
+
}
|
|
498
705
|
if (projection.attention_work.length) {
|
|
499
706
|
lines.push(`attention: ${projection.attention_work.join(", ")}`);
|
|
500
707
|
}
|
|
@@ -515,6 +722,7 @@ function emitSupervisorReport(
|
|
|
515
722
|
`supervisor ${report.goal_id}: ${report.stop_reason}\n` +
|
|
516
723
|
`reason: ${report.reason}\n` +
|
|
517
724
|
`cycles: ${report.cycles}; dispatches: ${report.dispatches}; acceptances: ${report.acceptances}\n` +
|
|
725
|
+
`replans: ${report.replans}\n` +
|
|
518
726
|
`state: ${report.projection.state}; next: ${report.projection.next_action}\n`,
|
|
519
727
|
);
|
|
520
728
|
}
|
|
@@ -531,6 +739,68 @@ function integer(value: string | undefined): number | undefined {
|
|
|
531
739
|
return Number.parseInt(value, 10);
|
|
532
740
|
}
|
|
533
741
|
|
|
742
|
+
function jsonInteger(value: unknown, field: string): number | undefined {
|
|
743
|
+
if (value === undefined) return undefined;
|
|
744
|
+
if (!Number.isSafeInteger(value) || (value as number) < 1) {
|
|
745
|
+
throw new Error(`${field} must be a positive integer`);
|
|
746
|
+
}
|
|
747
|
+
return value as number;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function jsonBoolean(value: unknown, field: string): boolean | undefined {
|
|
751
|
+
if (value === undefined) return undefined;
|
|
752
|
+
if (typeof value !== "boolean") throw new Error(`${field} must be boolean`);
|
|
753
|
+
return value;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function emitPlan(plan: SupervisorPlanRecord, json: boolean | undefined, emit: EmitContext): void {
|
|
757
|
+
if (json) {
|
|
758
|
+
emit.config({ format: "json" });
|
|
759
|
+
emit.data(plan);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const lines = [
|
|
763
|
+
`${plan.request.id}: ${plan.status}`,
|
|
764
|
+
`goal: ${plan.request.goal_id}`,
|
|
765
|
+
`sequence: ${plan.request.sequence}`,
|
|
766
|
+
`trigger: ${plan.request.trigger ?? "recovery"}`,
|
|
767
|
+
`prior root: ${plan.request.prior_root_work_id}`,
|
|
768
|
+
`planner run: ${plan.request.workflow_run_id}`,
|
|
769
|
+
];
|
|
770
|
+
if (plan.proposal) {
|
|
771
|
+
lines.push(`decision: ${plan.proposal.decision}`);
|
|
772
|
+
lines.push(`rationale: ${plan.proposal.rationale}`);
|
|
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
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (plan.root_work_id) lines.push(`applied root: ${plan.root_work_id}`);
|
|
779
|
+
if (plan.approval_id) lines.push(`approval: ${plan.approval_id}`);
|
|
780
|
+
emit.text(`${lines.join("\n")}\n`);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function emitPlanOutcome(
|
|
784
|
+
outcome: SupervisorPlanOutcome,
|
|
785
|
+
json: boolean | undefined,
|
|
786
|
+
emit: EmitContext,
|
|
787
|
+
): void {
|
|
788
|
+
if (json) {
|
|
789
|
+
emit.config({ format: "json" });
|
|
790
|
+
emit.data(outcome);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
emit.text(
|
|
794
|
+
`supervisor plan ${outcome.plan_id}: ${outcome.status}\n` +
|
|
795
|
+
`${outcome.reason ? `reason: ${outcome.reason}\n` : ""}` +
|
|
796
|
+
`${outcome.root_work_id ? `root: ${outcome.root_work_id}\n` : ""}`,
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function renderPlanRow(plan: SupervisorPlanRecord): string {
|
|
801
|
+
return `${plan.request.id} ${plan.status.padEnd(18)} ${plan.request.prior_root_work_id}`;
|
|
802
|
+
}
|
|
803
|
+
|
|
534
804
|
function delay(ms: number): Promise<void> {
|
|
535
805
|
return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
|
|
536
806
|
}
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
export { approveSupervisorPlan, rejectSupervisorPlan } from "./plan-api.ts";
|
|
2
|
+
export { readSupervisorPlan, readSupervisorPlans } from "./plan-read.ts";
|
|
3
|
+
export {
|
|
4
|
+
type CreateSupervisorPlanTemplateInput,
|
|
5
|
+
type CreateSupervisorReplanningInput,
|
|
6
|
+
SUPERVISOR_PLAN_SCHEMA_VERSION,
|
|
7
|
+
type SupervisorPlanEvent,
|
|
8
|
+
type SupervisorPlanEventType,
|
|
9
|
+
type SupervisorPlanHistory,
|
|
10
|
+
type SupervisorPlanMilestone,
|
|
11
|
+
type SupervisorPlanOutcome,
|
|
12
|
+
type SupervisorPlanProposal,
|
|
13
|
+
type SupervisorPlanRecord,
|
|
14
|
+
type SupervisorPlanRequest,
|
|
15
|
+
type SupervisorPlanStatus,
|
|
16
|
+
type SupervisorPlanTemplate,
|
|
17
|
+
type SupervisorPlanWorkSpec,
|
|
18
|
+
type SupervisorReplanningPolicy,
|
|
19
|
+
} from "./plan-types.ts";
|
|
1
20
|
export {
|
|
2
21
|
type RunSupervisorInput,
|
|
3
22
|
runSupervisor,
|
|
@@ -35,6 +54,7 @@ export {
|
|
|
35
54
|
export {
|
|
36
55
|
assertSupervisorId,
|
|
37
56
|
type CreateSupervisorInput,
|
|
57
|
+
type CreateSupervisorMissionInput,
|
|
38
58
|
collectSupervisorWork,
|
|
39
59
|
createSupervisor,
|
|
40
60
|
listSupervisors,
|
|
@@ -44,6 +64,7 @@ export {
|
|
|
44
64
|
type SupervisorAutomationPolicy,
|
|
45
65
|
type SupervisorIntent,
|
|
46
66
|
type SupervisorLimits,
|
|
67
|
+
type SupervisorMission,
|
|
47
68
|
type SupervisorNextAction,
|
|
48
69
|
type SupervisorProjection,
|
|
49
70
|
type SupervisorRecord,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { SupervisorPlanOutcome } from "./plan-types.ts";
|
|
2
|
+
import { applySupervisorPlanProposal, rejectSupervisorPlanProposal } from "./planning.ts";
|
|
3
|
+
import { acquireSupervisorLease, readSupervisorIgnoringLease } from "./state.ts";
|
|
4
|
+
|
|
5
|
+
export function approveSupervisorPlan(input: {
|
|
6
|
+
coordRoot: string;
|
|
7
|
+
goalId: string;
|
|
8
|
+
planId: string;
|
|
9
|
+
actor?: string;
|
|
10
|
+
reason?: string;
|
|
11
|
+
}): SupervisorPlanOutcome {
|
|
12
|
+
const actor = input.actor?.trim() || `supervisor:${input.goalId}`;
|
|
13
|
+
const release = acquireSupervisorLease(input.coordRoot, input.goalId);
|
|
14
|
+
try {
|
|
15
|
+
return applySupervisorPlanProposal({
|
|
16
|
+
coordRoot: input.coordRoot,
|
|
17
|
+
record: readSupervisorIgnoringLease(input.coordRoot, input.goalId),
|
|
18
|
+
planId: input.planId,
|
|
19
|
+
actor,
|
|
20
|
+
reason: input.reason,
|
|
21
|
+
});
|
|
22
|
+
} finally {
|
|
23
|
+
release();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function rejectSupervisorPlan(input: {
|
|
28
|
+
coordRoot: string;
|
|
29
|
+
goalId: string;
|
|
30
|
+
planId: string;
|
|
31
|
+
actor?: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
}): SupervisorPlanOutcome {
|
|
34
|
+
const actor = input.actor?.trim() || `supervisor:${input.goalId}`;
|
|
35
|
+
const release = acquireSupervisorLease(input.coordRoot, input.goalId);
|
|
36
|
+
try {
|
|
37
|
+
return rejectSupervisorPlanProposal({
|
|
38
|
+
coordRoot: input.coordRoot,
|
|
39
|
+
goalId: input.goalId,
|
|
40
|
+
planId: input.planId,
|
|
41
|
+
actor,
|
|
42
|
+
reason: input.reason,
|
|
43
|
+
});
|
|
44
|
+
} finally {
|
|
45
|
+
release();
|
|
46
|
+
}
|
|
47
|
+
}
|