harnery 0.16.0 → 0.17.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 +184 -3
- package/dist/core/supervisor/index.d.ts +3 -0
- 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 +269 -0
- package/dist/core/supervisor/plan-types.d.ts +113 -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 +486 -0
- package/dist/core/supervisor/read.d.ts +2 -0
- 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 +37 -7
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +7 -0
- package/dist/core/supervisor/state.d.ts +11 -1
- package/dist/core/supervisor/state.d.ts.map +1 -1
- package/dist/core/supervisor/state.js +186 -7
- package/package.json +1 -1
- package/src/commands/supervisor.ts +229 -2
- package/src/core/supervisor/index.ts +18 -0
- package/src/core/supervisor/plan-api.ts +47 -0
- package/src/core/supervisor/plan-read.ts +299 -0
- package/src/core/supervisor/plan-types.ts +144 -0
- package/src/core/supervisor/planning.ts +576 -0
- package/src/core/supervisor/read.ts +15 -0
- package/src/core/supervisor/runner.ts +39 -2
- package/src/core/supervisor/service.ts +7 -0
- package/src/core/supervisor/state.ts +235 -6
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export declare const SUPERVISOR_PLAN_SCHEMA_VERSION: 1;
|
|
2
|
+
export interface SupervisorPlanTemplate {
|
|
3
|
+
workflow: {
|
|
4
|
+
path: string;
|
|
5
|
+
sha256: string;
|
|
6
|
+
};
|
|
7
|
+
max_attempts: number;
|
|
8
|
+
root: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface SupervisorReplanningPolicy {
|
|
11
|
+
planner_specialist: string;
|
|
12
|
+
auto_apply: boolean;
|
|
13
|
+
max_replans: number;
|
|
14
|
+
max_work_items_per_plan: number;
|
|
15
|
+
max_total_work_items: number;
|
|
16
|
+
templates: Record<string, SupervisorPlanTemplate>;
|
|
17
|
+
}
|
|
18
|
+
export interface CreateSupervisorPlanTemplateInput {
|
|
19
|
+
workflowPath: string;
|
|
20
|
+
maxAttempts?: number;
|
|
21
|
+
root?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface CreateSupervisorReplanningInput {
|
|
24
|
+
plannerSpecialist: string;
|
|
25
|
+
autoApply?: boolean;
|
|
26
|
+
maxReplans?: number;
|
|
27
|
+
maxWorkItemsPerPlan?: number;
|
|
28
|
+
maxTotalWorkItems?: number;
|
|
29
|
+
templates: Readonly<Record<string, CreateSupervisorPlanTemplateInput>>;
|
|
30
|
+
}
|
|
31
|
+
export interface SupervisorPlanRequest {
|
|
32
|
+
schema_version: typeof SUPERVISOR_PLAN_SCHEMA_VERSION;
|
|
33
|
+
id: string;
|
|
34
|
+
goal_id: string;
|
|
35
|
+
sequence: number;
|
|
36
|
+
trigger_fingerprint: string;
|
|
37
|
+
prior_root_work_id: string;
|
|
38
|
+
workflow_run_id: string;
|
|
39
|
+
created_at: string;
|
|
40
|
+
}
|
|
41
|
+
export interface SupervisorPlanWorkSpec {
|
|
42
|
+
key: string;
|
|
43
|
+
title: string;
|
|
44
|
+
objective: string;
|
|
45
|
+
acceptance: string[];
|
|
46
|
+
dependencies: string[];
|
|
47
|
+
template: string;
|
|
48
|
+
}
|
|
49
|
+
export interface SupervisorPlanProposal {
|
|
50
|
+
schema_version: typeof SUPERVISOR_PLAN_SCHEMA_VERSION;
|
|
51
|
+
plan_id: string;
|
|
52
|
+
decision: "apply" | "attention";
|
|
53
|
+
rationale: string;
|
|
54
|
+
root: string;
|
|
55
|
+
work: SupervisorPlanWorkSpec[];
|
|
56
|
+
proposed_at: string;
|
|
57
|
+
}
|
|
58
|
+
export type SupervisorPlanEventType = "plan.awaiting_approval" | "plan.resumed" | "plan.proposed" | "plan.applied" | "plan.rejected" | "plan.attention" | "plan.failed";
|
|
59
|
+
export interface SupervisorPlanEvent {
|
|
60
|
+
schema_version: typeof SUPERVISOR_PLAN_SCHEMA_VERSION;
|
|
61
|
+
plan_id: string;
|
|
62
|
+
seq: number;
|
|
63
|
+
ts: string;
|
|
64
|
+
event: SupervisorPlanEventType;
|
|
65
|
+
actor: string;
|
|
66
|
+
reason: string;
|
|
67
|
+
approval_id?: string;
|
|
68
|
+
root_work_id?: string;
|
|
69
|
+
work_ids?: string[];
|
|
70
|
+
}
|
|
71
|
+
export type SupervisorPlanStatus = "interrupted" | "awaiting_approval" | "resumable" | "proposed" | "applied" | "rejected" | "attention" | "failed";
|
|
72
|
+
export interface SupervisorPlanRecord {
|
|
73
|
+
request: SupervisorPlanRequest;
|
|
74
|
+
proposal?: SupervisorPlanProposal;
|
|
75
|
+
events: SupervisorPlanEvent[];
|
|
76
|
+
status: SupervisorPlanStatus;
|
|
77
|
+
approval_id?: string;
|
|
78
|
+
root_work_id?: string;
|
|
79
|
+
work_ids: string[];
|
|
80
|
+
reason?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface SupervisorPlanHistory {
|
|
83
|
+
plans: SupervisorPlanRecord[];
|
|
84
|
+
active_root_work_id: string;
|
|
85
|
+
generation: number;
|
|
86
|
+
applied_work_ids: string[];
|
|
87
|
+
materialized_work_ids: string[];
|
|
88
|
+
latest?: SupervisorPlanRecord;
|
|
89
|
+
}
|
|
90
|
+
export interface SupervisorPlanOutcome {
|
|
91
|
+
plan_id: string;
|
|
92
|
+
status: SupervisorPlanStatus;
|
|
93
|
+
workflow_run_id: string;
|
|
94
|
+
reason?: string;
|
|
95
|
+
root_work_id?: string;
|
|
96
|
+
work_ids: string[];
|
|
97
|
+
}
|
|
98
|
+
export declare function supervisorGraphFingerprint(input: {
|
|
99
|
+
rootWorkId: string;
|
|
100
|
+
generation: number;
|
|
101
|
+
work: ReadonlyArray<{
|
|
102
|
+
intent: {
|
|
103
|
+
id: string;
|
|
104
|
+
};
|
|
105
|
+
projection: {
|
|
106
|
+
state: string;
|
|
107
|
+
next_action: string;
|
|
108
|
+
attempts_used: number;
|
|
109
|
+
};
|
|
110
|
+
events: readonly unknown[];
|
|
111
|
+
}>;
|
|
112
|
+
}): string;
|
|
113
|
+
//# sourceMappingURL=plan-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-types.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/plan-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const SUPERVISOR_PLAN_SCHEMA_VERSION = 1;
|
|
2
|
+
export function supervisorGraphFingerprint(input) {
|
|
3
|
+
return JSON.stringify([
|
|
4
|
+
input.rootWorkId,
|
|
5
|
+
input.generation,
|
|
6
|
+
input.work.map((work) => [
|
|
7
|
+
work.intent.id,
|
|
8
|
+
work.projection.state,
|
|
9
|
+
work.projection.next_action,
|
|
10
|
+
work.projection.attempts_used,
|
|
11
|
+
work.events.length,
|
|
12
|
+
]),
|
|
13
|
+
]);
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type EngineOpts } from "../workflow/index.js";
|
|
2
|
+
import { type SupervisorPlanOutcome } from "./plan-types.js";
|
|
3
|
+
import type { SupervisorRecord } from "./state.js";
|
|
4
|
+
export interface RunSupervisorPlannerInput {
|
|
5
|
+
coordRoot: string;
|
|
6
|
+
record: SupervisorRecord;
|
|
7
|
+
engine: Omit<EngineOpts, "coordRoot" | "runId" | "resumeRunId" | "specialists">;
|
|
8
|
+
actor: string;
|
|
9
|
+
onLog?: (line: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function runSupervisorPlanner(input: RunSupervisorPlannerInput): Promise<SupervisorPlanOutcome>;
|
|
12
|
+
export declare function applySupervisorPlanProposal(input: {
|
|
13
|
+
coordRoot: string;
|
|
14
|
+
record: SupervisorRecord;
|
|
15
|
+
planId: string;
|
|
16
|
+
actor: string;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}): SupervisorPlanOutcome;
|
|
19
|
+
export declare function rejectSupervisorPlanProposal(input: {
|
|
20
|
+
coordRoot: string;
|
|
21
|
+
goalId: string;
|
|
22
|
+
planId: string;
|
|
23
|
+
actor: string;
|
|
24
|
+
reason: string;
|
|
25
|
+
}): SupervisorPlanOutcome;
|
|
26
|
+
//# sourceMappingURL=planning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/planning.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAGL,KAAK,qBAAqB,EAK3B,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAQnD,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,GAAG,OAAO,GAAG,aAAa,GAAG,aAAa,CAAC,CAAC;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAkGhC;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,qBAAqB,CAyFxB;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,qBAAqB,CAYxB"}
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
|
+
import { appendFileSync, chmodSync, existsSync, linkSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { createWorkItem, readWorkItem } from "../work/index.js";
|
|
5
|
+
import { runWorkflow, WorkflowParkedError, workflowScriptDigest, } from "../workflow/index.js";
|
|
6
|
+
import { readSupervisorPlan, readSupervisorPlans } from "./plan-read.js";
|
|
7
|
+
import { SUPERVISOR_PLAN_SCHEMA_VERSION, supervisorGraphFingerprint, } from "./plan-types.js";
|
|
8
|
+
const PLAN_KEY = /^[a-z][a-z0-9-]{0,31}$/;
|
|
9
|
+
const TEMPLATE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
10
|
+
const MAX_RECORD_BYTES = 256 * 1024;
|
|
11
|
+
const MAX_EVENTS_BYTES = 512 * 1024;
|
|
12
|
+
const MAX_REASON = 2_000;
|
|
13
|
+
export async function runSupervisorPlanner(input) {
|
|
14
|
+
const coordRoot = resolve(input.coordRoot);
|
|
15
|
+
const policy = input.record.intent.replanning;
|
|
16
|
+
if (!policy)
|
|
17
|
+
throw new Error(`supervisor ${input.record.intent.id} does not allow replanning`);
|
|
18
|
+
const history = readSupervisorPlans(coordRoot, input.record.intent.id, input.record.intent.root_work_id);
|
|
19
|
+
const triggerFingerprint = supervisorGraphFingerprint({
|
|
20
|
+
rootWorkId: input.record.projection.root_work_id,
|
|
21
|
+
generation: input.record.projection.plan_generation,
|
|
22
|
+
work: input.record.work,
|
|
23
|
+
});
|
|
24
|
+
const resumable = history.latest?.status === "resumable" &&
|
|
25
|
+
history.latest.request.trigger_fingerprint === triggerFingerprint &&
|
|
26
|
+
history.latest.request.prior_root_work_id === input.record.projection.root_work_id
|
|
27
|
+
? history.latest
|
|
28
|
+
: undefined;
|
|
29
|
+
const request = resumable
|
|
30
|
+
? resumable.request
|
|
31
|
+
: createPlanRequest(coordRoot, input.record, history.plans.length + 1, triggerFingerprint);
|
|
32
|
+
const scriptPath = planScriptPath(coordRoot, input.record.intent.id, request.id);
|
|
33
|
+
if (resumable) {
|
|
34
|
+
appendPlanEvent(coordRoot, input.record.intent.id, request.id, {
|
|
35
|
+
event: "plan.resumed",
|
|
36
|
+
actor: input.actor,
|
|
37
|
+
reason: `resuming planner workflow after approval ${resumable.approval_id}`,
|
|
38
|
+
approval_id: resumable.approval_id,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
let proposalWritten = false;
|
|
42
|
+
try {
|
|
43
|
+
const report = await runWorkflow(scriptPath, {
|
|
44
|
+
...input.engine,
|
|
45
|
+
coordRoot,
|
|
46
|
+
...(resumable
|
|
47
|
+
? { resumeRunId: request.workflow_run_id }
|
|
48
|
+
: { runId: request.workflow_run_id }),
|
|
49
|
+
specialists: input.record.intent.specialists,
|
|
50
|
+
maxAgents: 1,
|
|
51
|
+
concurrency: 1,
|
|
52
|
+
onLog: input.onLog,
|
|
53
|
+
});
|
|
54
|
+
const proposal = normalizeProposal(request.id, report.result, input.record, history.materialized_work_ids.length);
|
|
55
|
+
writeExclusiveJson(planProposalPath(coordRoot, input.record.intent.id, request.id), proposal, "supervisor plan proposal");
|
|
56
|
+
appendPlanEvent(coordRoot, input.record.intent.id, request.id, {
|
|
57
|
+
event: "plan.proposed",
|
|
58
|
+
actor: input.actor,
|
|
59
|
+
reason: proposal.rationale,
|
|
60
|
+
});
|
|
61
|
+
proposalWritten = true;
|
|
62
|
+
if (proposal.decision === "attention") {
|
|
63
|
+
appendPlanEvent(coordRoot, input.record.intent.id, request.id, {
|
|
64
|
+
event: "plan.attention",
|
|
65
|
+
actor: input.actor,
|
|
66
|
+
reason: proposal.rationale,
|
|
67
|
+
});
|
|
68
|
+
return outcome(readSupervisorPlan(coordRoot, input.record.intent.id, request.id));
|
|
69
|
+
}
|
|
70
|
+
if (policy.auto_apply) {
|
|
71
|
+
return applySupervisorPlanProposal({
|
|
72
|
+
coordRoot,
|
|
73
|
+
record: input.record,
|
|
74
|
+
planId: request.id,
|
|
75
|
+
actor: input.actor,
|
|
76
|
+
reason: "proposal applied by frozen supervisor replanning policy",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return outcome(readSupervisorPlan(coordRoot, input.record.intent.id, request.id));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (proposalWritten)
|
|
83
|
+
throw error;
|
|
84
|
+
if (error instanceof WorkflowParkedError) {
|
|
85
|
+
appendPlanEvent(coordRoot, input.record.intent.id, request.id, {
|
|
86
|
+
event: "plan.awaiting_approval",
|
|
87
|
+
actor: input.actor,
|
|
88
|
+
reason: `planner workflow parked for approval ${error.approvalId}`,
|
|
89
|
+
approval_id: error.approvalId,
|
|
90
|
+
});
|
|
91
|
+
return outcome(readSupervisorPlan(coordRoot, input.record.intent.id, request.id));
|
|
92
|
+
}
|
|
93
|
+
appendPlanEvent(coordRoot, input.record.intent.id, request.id, {
|
|
94
|
+
event: "plan.failed",
|
|
95
|
+
actor: input.actor,
|
|
96
|
+
reason: bounded(error.message, "plan failure", MAX_REASON),
|
|
97
|
+
});
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function applySupervisorPlanProposal(input) {
|
|
102
|
+
const coordRoot = resolve(input.coordRoot);
|
|
103
|
+
const policy = input.record.intent.replanning;
|
|
104
|
+
if (!policy)
|
|
105
|
+
throw new Error(`supervisor ${input.record.intent.id} does not allow replanning`);
|
|
106
|
+
const plan = readSupervisorPlan(coordRoot, input.record.intent.id, input.planId);
|
|
107
|
+
if (plan.status === "applied")
|
|
108
|
+
return outcome(plan);
|
|
109
|
+
if (plan.status !== "proposed" || !plan.proposal || plan.proposal.decision !== "apply") {
|
|
110
|
+
throw new Error(`supervisor plan ${input.planId} cannot be applied from ${plan.status}`);
|
|
111
|
+
}
|
|
112
|
+
if (plan.request.prior_root_work_id !== input.record.projection.root_work_id) {
|
|
113
|
+
throw new Error(`supervisor plan ${input.planId} targets stale root ${plan.request.prior_root_work_id}`);
|
|
114
|
+
}
|
|
115
|
+
const history = readSupervisorPlans(coordRoot, input.record.intent.id, input.record.intent.root_work_id);
|
|
116
|
+
const proposal = normalizeProposal(input.planId, plan.proposal, input.record, history.materialized_work_ids.filter((workId) => !workId.startsWith(`${input.planId}-`)).length);
|
|
117
|
+
const ids = new Map();
|
|
118
|
+
const created = [];
|
|
119
|
+
for (const spec of proposal.work) {
|
|
120
|
+
const template = policy.templates[spec.template];
|
|
121
|
+
if (!template)
|
|
122
|
+
throw new Error(`plan work ${spec.key} references unknown template ${spec.template}`);
|
|
123
|
+
if (workflowScriptDigest(template.workflow.path) !== template.workflow.sha256) {
|
|
124
|
+
throw new Error(`replanning template ${spec.template} changed after goal creation`);
|
|
125
|
+
}
|
|
126
|
+
const workId = `${input.planId}-${spec.key}`;
|
|
127
|
+
const dependencies = spec.dependencies.map((dependency) => ids.get(dependency) ?? dependency);
|
|
128
|
+
const expected = {
|
|
129
|
+
title: spec.title,
|
|
130
|
+
objective: spec.objective,
|
|
131
|
+
acceptance: spec.acceptance,
|
|
132
|
+
dependencies,
|
|
133
|
+
workflow: template.workflow,
|
|
134
|
+
max_attempts: template.max_attempts,
|
|
135
|
+
source: {
|
|
136
|
+
kind: "workflow",
|
|
137
|
+
ref: `supervisor:${input.record.intent.id}/plan:${input.planId}`,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
if (existsSync(join(coordRoot, ".harnery", "work", workId, "intent.json"))) {
|
|
141
|
+
const current = readWorkItem(coordRoot, workId).intent;
|
|
142
|
+
const actual = {
|
|
143
|
+
title: current.title,
|
|
144
|
+
objective: current.objective,
|
|
145
|
+
acceptance: current.acceptance,
|
|
146
|
+
dependencies: current.dependencies,
|
|
147
|
+
workflow: current.workflow,
|
|
148
|
+
max_attempts: current.max_attempts,
|
|
149
|
+
source: current.source,
|
|
150
|
+
};
|
|
151
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
152
|
+
throw new Error(`partial plan work ${workId} conflicts with the durable proposal`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
createWorkItem({
|
|
157
|
+
coordRoot,
|
|
158
|
+
id: workId,
|
|
159
|
+
title: spec.title,
|
|
160
|
+
objective: spec.objective,
|
|
161
|
+
acceptance: spec.acceptance,
|
|
162
|
+
dependencies,
|
|
163
|
+
workflowPath: template.workflow.path,
|
|
164
|
+
maxAttempts: template.max_attempts,
|
|
165
|
+
source: expected.source,
|
|
166
|
+
actor: input.actor,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
ids.set(spec.key, workId);
|
|
170
|
+
created.push(workId);
|
|
171
|
+
}
|
|
172
|
+
const rootWorkId = ids.get(proposal.root);
|
|
173
|
+
if (!rootWorkId)
|
|
174
|
+
throw new Error(`plan root ${proposal.root} was not materialized`);
|
|
175
|
+
appendPlanEvent(coordRoot, input.record.intent.id, input.planId, {
|
|
176
|
+
event: "plan.applied",
|
|
177
|
+
actor: input.actor,
|
|
178
|
+
reason: input.reason ?? "supervisor plan explicitly approved",
|
|
179
|
+
root_work_id: rootWorkId,
|
|
180
|
+
work_ids: created,
|
|
181
|
+
});
|
|
182
|
+
return outcome(readSupervisorPlan(coordRoot, input.record.intent.id, input.planId));
|
|
183
|
+
}
|
|
184
|
+
export function rejectSupervisorPlanProposal(input) {
|
|
185
|
+
const plan = readSupervisorPlan(input.coordRoot, input.goalId, input.planId);
|
|
186
|
+
if (plan.status === "rejected")
|
|
187
|
+
return outcome(plan);
|
|
188
|
+
if (plan.status !== "proposed") {
|
|
189
|
+
throw new Error(`supervisor plan ${input.planId} cannot be rejected from ${plan.status}`);
|
|
190
|
+
}
|
|
191
|
+
appendPlanEvent(input.coordRoot, input.goalId, input.planId, {
|
|
192
|
+
event: "plan.rejected",
|
|
193
|
+
actor: input.actor,
|
|
194
|
+
reason: input.reason,
|
|
195
|
+
});
|
|
196
|
+
return outcome(readSupervisorPlan(input.coordRoot, input.goalId, input.planId));
|
|
197
|
+
}
|
|
198
|
+
function createPlanRequest(coordRoot, record, sequence, triggerFingerprint) {
|
|
199
|
+
const policy = record.intent.replanning;
|
|
200
|
+
if (!policy)
|
|
201
|
+
throw new Error(`supervisor ${record.intent.id} does not allow replanning`);
|
|
202
|
+
if (sequence > policy.max_replans) {
|
|
203
|
+
throw new Error(`supervisor ${record.intent.id} exhausted its ${policy.max_replans} replans`);
|
|
204
|
+
}
|
|
205
|
+
const planId = `plan-${String(sequence).padStart(4, "0")}-${randomBytes(4).toString("hex")}`;
|
|
206
|
+
const request = {
|
|
207
|
+
schema_version: SUPERVISOR_PLAN_SCHEMA_VERSION,
|
|
208
|
+
id: planId,
|
|
209
|
+
goal_id: record.intent.id,
|
|
210
|
+
sequence,
|
|
211
|
+
trigger_fingerprint: triggerFingerprint,
|
|
212
|
+
prior_root_work_id: record.projection.root_work_id,
|
|
213
|
+
workflow_run_id: `${planId}-workflow`,
|
|
214
|
+
created_at: new Date().toISOString(),
|
|
215
|
+
};
|
|
216
|
+
const dir = planDir(coordRoot, record.intent.id, planId);
|
|
217
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
218
|
+
chmodSync(dir, 0o700);
|
|
219
|
+
writeExclusiveJson(join(dir, "request.json"), request, "supervisor plan request");
|
|
220
|
+
const script = plannerScript(record, planId);
|
|
221
|
+
writeExclusive(join(dir, "planner.mjs"), script, "supervisor planner script");
|
|
222
|
+
return request;
|
|
223
|
+
}
|
|
224
|
+
function plannerScript(record, planId) {
|
|
225
|
+
const policy = record.intent.replanning;
|
|
226
|
+
if (!policy)
|
|
227
|
+
throw new Error(`supervisor ${record.intent.id} does not allow replanning`);
|
|
228
|
+
const prompt = [
|
|
229
|
+
"You are producing a bounded replacement plan for a durable supervised goal.",
|
|
230
|
+
"The current graph is blocked and cannot make a legal scheduling step.",
|
|
231
|
+
"Return decision=apply with a new immutable root graph, or decision=attention when human judgment is required.",
|
|
232
|
+
"Never claim work is complete. Never request a workflow outside the frozen template catalog.",
|
|
233
|
+
"Dependencies may name an active work ID or an earlier key in your proposed work array.",
|
|
234
|
+
"Every proposed item must be reachable from root. The root must be a proposed key using a root-capable template.",
|
|
235
|
+
`Plan id: ${planId}`,
|
|
236
|
+
`Goal: ${record.intent.title}`,
|
|
237
|
+
`Original root: ${record.intent.root_work_id}`,
|
|
238
|
+
`Active root: ${record.projection.root_work_id}`,
|
|
239
|
+
`Remaining work attempts: ${record.projection.attempts_remaining}`,
|
|
240
|
+
`Remaining replans: ${record.projection.replans_remaining}`,
|
|
241
|
+
`Latest planner outcome: ${JSON.stringify(record.plans.at(-1)
|
|
242
|
+
? {
|
|
243
|
+
status: record.plans.at(-1)?.status,
|
|
244
|
+
reason: record.plans.at(-1)?.reason,
|
|
245
|
+
}
|
|
246
|
+
: null)}`,
|
|
247
|
+
`Templates: ${JSON.stringify(policy.templates)}`,
|
|
248
|
+
`Active work: ${JSON.stringify(record.work.map((work) => ({
|
|
249
|
+
id: work.intent.id,
|
|
250
|
+
title: work.intent.title,
|
|
251
|
+
objective: work.intent.objective,
|
|
252
|
+
acceptance: work.intent.acceptance,
|
|
253
|
+
dependencies: work.intent.dependencies,
|
|
254
|
+
state: work.projection.state,
|
|
255
|
+
reason: work.projection.reason,
|
|
256
|
+
})))}`,
|
|
257
|
+
].join("\n\n");
|
|
258
|
+
const schema = {
|
|
259
|
+
type: "object",
|
|
260
|
+
properties: {
|
|
261
|
+
decision: { type: "string", enum: ["apply", "attention"] },
|
|
262
|
+
rationale: { type: "string" },
|
|
263
|
+
root: { type: "string" },
|
|
264
|
+
work: {
|
|
265
|
+
type: "array",
|
|
266
|
+
items: {
|
|
267
|
+
type: "object",
|
|
268
|
+
properties: {
|
|
269
|
+
key: { type: "string" },
|
|
270
|
+
title: { type: "string" },
|
|
271
|
+
objective: { type: "string" },
|
|
272
|
+
acceptance: { type: "array", items: { type: "string" } },
|
|
273
|
+
dependencies: { type: "array", items: { type: "string" } },
|
|
274
|
+
template: { type: "string" },
|
|
275
|
+
},
|
|
276
|
+
required: ["key", "title", "objective", "acceptance", "dependencies", "template"],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
required: ["decision", "rationale", "root", "work"],
|
|
281
|
+
};
|
|
282
|
+
return [
|
|
283
|
+
`export const meta = ${JSON.stringify({ name: `replan-${planId}` })};`,
|
|
284
|
+
"export default async ({ agent, stage }) => {",
|
|
285
|
+
' stage("Replan blocked goal");',
|
|
286
|
+
` return await agent(${JSON.stringify(prompt)}, ${JSON.stringify({
|
|
287
|
+
specialist: policy.planner_specialist,
|
|
288
|
+
schema,
|
|
289
|
+
label: "Produce bounded replacement plan",
|
|
290
|
+
})});`,
|
|
291
|
+
"};",
|
|
292
|
+
"",
|
|
293
|
+
].join("\n");
|
|
294
|
+
}
|
|
295
|
+
function normalizeProposal(planId, raw, record, previouslyApplied) {
|
|
296
|
+
const policy = record.intent.replanning;
|
|
297
|
+
if (!policy)
|
|
298
|
+
throw new Error(`supervisor ${record.intent.id} does not allow replanning`);
|
|
299
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
300
|
+
throw new Error("planner result must be an object");
|
|
301
|
+
}
|
|
302
|
+
const value = raw;
|
|
303
|
+
const decision = enumValue(value.decision, ["apply", "attention"], "plan decision");
|
|
304
|
+
const rationale = bounded(value.rationale, "plan rationale", MAX_REASON);
|
|
305
|
+
const root = typeof value.root === "string" ? value.root.trim() : "";
|
|
306
|
+
if (!Array.isArray(value.work))
|
|
307
|
+
throw new Error("plan work must be an array");
|
|
308
|
+
if (decision === "attention") {
|
|
309
|
+
if (root || value.work.length > 0)
|
|
310
|
+
throw new Error("attention plan must not contain work");
|
|
311
|
+
return {
|
|
312
|
+
schema_version: SUPERVISOR_PLAN_SCHEMA_VERSION,
|
|
313
|
+
plan_id: planId,
|
|
314
|
+
decision,
|
|
315
|
+
rationale,
|
|
316
|
+
root: "",
|
|
317
|
+
work: [],
|
|
318
|
+
proposed_at: new Date().toISOString(),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
if (value.work.length < 1 || value.work.length > policy.max_work_items_per_plan) {
|
|
322
|
+
throw new Error(`plan work must contain 1 to ${policy.max_work_items_per_plan} items`);
|
|
323
|
+
}
|
|
324
|
+
if (previouslyApplied + value.work.length > policy.max_total_work_items) {
|
|
325
|
+
throw new Error(`plan would exceed ${policy.max_total_work_items} total planned work items`);
|
|
326
|
+
}
|
|
327
|
+
const active = new Set(record.projection.work_ids);
|
|
328
|
+
const seen = new Set();
|
|
329
|
+
const work = value.work.map((candidate, index) => {
|
|
330
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
331
|
+
throw new Error(`plan work[${index}] must be an object`);
|
|
332
|
+
}
|
|
333
|
+
const item = candidate;
|
|
334
|
+
const key = bounded(item.key, `plan work[${index}].key`, 32);
|
|
335
|
+
if (!PLAN_KEY.test(key) || seen.has(key))
|
|
336
|
+
throw new Error(`invalid or duplicate plan key ${key}`);
|
|
337
|
+
const template = bounded(item.template, `plan work[${index}].template`, 64);
|
|
338
|
+
if (!TEMPLATE_ID.test(template) || !policy.templates[template]) {
|
|
339
|
+
throw new Error(`plan work ${key} references unknown template ${template}`);
|
|
340
|
+
}
|
|
341
|
+
if (!Array.isArray(item.dependencies))
|
|
342
|
+
throw new Error(`plan work ${key} dependencies must be an array`);
|
|
343
|
+
const dependencies = [
|
|
344
|
+
...new Set(item.dependencies.map((dependency, dependencyIndex) => bounded(dependency, `plan work ${key} dependency[${dependencyIndex}]`, 100))),
|
|
345
|
+
];
|
|
346
|
+
if (dependencies.length > 50)
|
|
347
|
+
throw new Error(`plan work ${key} exceeds 50 dependencies`);
|
|
348
|
+
for (const dependency of dependencies) {
|
|
349
|
+
if (!active.has(dependency) && !seen.has(dependency)) {
|
|
350
|
+
throw new Error(`plan work ${key} dependency ${dependency} is not active or earlier in the plan`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!Array.isArray(item.acceptance))
|
|
354
|
+
throw new Error(`plan work ${key} acceptance must be an array`);
|
|
355
|
+
const acceptance = item.acceptance.map((criterion, criterionIndex) => bounded(criterion, `plan work ${key} acceptance[${criterionIndex}]`, 500));
|
|
356
|
+
if (acceptance.length > 50)
|
|
357
|
+
throw new Error(`plan work ${key} exceeds 50 acceptance criteria`);
|
|
358
|
+
seen.add(key);
|
|
359
|
+
return {
|
|
360
|
+
key,
|
|
361
|
+
title: bounded(item.title, `plan work ${key} title`, 200),
|
|
362
|
+
objective: bounded(item.objective, `plan work ${key} objective`, 4_000),
|
|
363
|
+
acceptance,
|
|
364
|
+
dependencies,
|
|
365
|
+
template,
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
if (!seen.has(root))
|
|
369
|
+
throw new Error(`plan root ${root || "(empty)"} is not a proposed key`);
|
|
370
|
+
const rootSpec = work.find((item) => item.key === root);
|
|
371
|
+
if (!rootSpec || !policy.templates[rootSpec.template]?.root) {
|
|
372
|
+
throw new Error(`plan root ${root} must use a root-capable template`);
|
|
373
|
+
}
|
|
374
|
+
const reachable = new Set();
|
|
375
|
+
const byKey = new Map(work.map((item) => [item.key, item]));
|
|
376
|
+
const visit = (key) => {
|
|
377
|
+
if (reachable.has(key))
|
|
378
|
+
return;
|
|
379
|
+
reachable.add(key);
|
|
380
|
+
for (const dependency of byKey.get(key)?.dependencies ?? []) {
|
|
381
|
+
if (byKey.has(dependency))
|
|
382
|
+
visit(dependency);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
visit(root);
|
|
386
|
+
const unused = work.filter((item) => !reachable.has(item.key));
|
|
387
|
+
if (unused.length)
|
|
388
|
+
throw new Error(`plan contains work not reachable from root: ${unused.map((item) => item.key).join(", ")}`);
|
|
389
|
+
return {
|
|
390
|
+
schema_version: SUPERVISOR_PLAN_SCHEMA_VERSION,
|
|
391
|
+
plan_id: planId,
|
|
392
|
+
decision,
|
|
393
|
+
rationale,
|
|
394
|
+
root,
|
|
395
|
+
work,
|
|
396
|
+
proposed_at: new Date().toISOString(),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function appendPlanEvent(coordRootRaw, goalId, planId, input) {
|
|
400
|
+
const coordRoot = resolve(coordRootRaw);
|
|
401
|
+
const path = join(planDir(coordRoot, goalId, planId), "events.jsonl");
|
|
402
|
+
const current = readSupervisorPlan(coordRoot, goalId, planId).events;
|
|
403
|
+
const event = {
|
|
404
|
+
schema_version: SUPERVISOR_PLAN_SCHEMA_VERSION,
|
|
405
|
+
plan_id: planId,
|
|
406
|
+
seq: current.length + 1,
|
|
407
|
+
ts: new Date().toISOString(),
|
|
408
|
+
...input,
|
|
409
|
+
actor: bounded(input.actor, "plan actor", 200),
|
|
410
|
+
reason: bounded(input.reason, "plan reason", MAX_REASON),
|
|
411
|
+
};
|
|
412
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
413
|
+
const existing = existsSync(path) ? statSync(path).size : 0;
|
|
414
|
+
if (existing + Buffer.byteLength(line) > MAX_EVENTS_BYTES) {
|
|
415
|
+
throw new Error("supervisor plan event log would exceed its byte limit");
|
|
416
|
+
}
|
|
417
|
+
appendFileSync(path, line, { encoding: "utf8", mode: 0o600 });
|
|
418
|
+
chmodSync(path, 0o600);
|
|
419
|
+
}
|
|
420
|
+
function outcome(plan) {
|
|
421
|
+
return {
|
|
422
|
+
plan_id: plan.request.id,
|
|
423
|
+
status: plan.status,
|
|
424
|
+
workflow_run_id: plan.request.workflow_run_id,
|
|
425
|
+
reason: plan.reason,
|
|
426
|
+
root_work_id: plan.root_work_id,
|
|
427
|
+
work_ids: plan.work_ids,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function writeExclusiveJson(path, value, label) {
|
|
431
|
+
writeExclusive(path, `${JSON.stringify(value, null, 2)}\n`, label);
|
|
432
|
+
}
|
|
433
|
+
function writeExclusive(path, body, label) {
|
|
434
|
+
if (Buffer.byteLength(body) > MAX_RECORD_BYTES) {
|
|
435
|
+
throw new Error(`${label} exceeds ${MAX_RECORD_BYTES} bytes`);
|
|
436
|
+
}
|
|
437
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
438
|
+
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
439
|
+
try {
|
|
440
|
+
writeFileSync(temporary, body, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
441
|
+
try {
|
|
442
|
+
linkSync(temporary, path);
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
if (error.code !== "EEXIST")
|
|
446
|
+
throw error;
|
|
447
|
+
const existing = readFileSync(path, "utf8");
|
|
448
|
+
if (existing !== body)
|
|
449
|
+
throw new Error(`${label} already exists with different content`);
|
|
450
|
+
}
|
|
451
|
+
chmodSync(path, 0o600);
|
|
452
|
+
}
|
|
453
|
+
finally {
|
|
454
|
+
try {
|
|
455
|
+
unlinkSync(temporary);
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
// The temporary may already be absent after a failed exclusive write.
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function planDir(coordRoot, goalId, planId) {
|
|
463
|
+
return join(coordRoot, ".harnery", "supervisors", goalId, "plans", planId);
|
|
464
|
+
}
|
|
465
|
+
function planScriptPath(coordRoot, goalId, planId) {
|
|
466
|
+
return join(planDir(coordRoot, goalId, planId), "planner.mjs");
|
|
467
|
+
}
|
|
468
|
+
function planProposalPath(coordRoot, goalId, planId) {
|
|
469
|
+
return join(planDir(coordRoot, goalId, planId), "proposal.json");
|
|
470
|
+
}
|
|
471
|
+
function bounded(value, field, max) {
|
|
472
|
+
if (typeof value !== "string")
|
|
473
|
+
throw new Error(`${field} must be a string`);
|
|
474
|
+
const normalized = value.trim();
|
|
475
|
+
if (!normalized)
|
|
476
|
+
throw new Error(`${field} must not be empty`);
|
|
477
|
+
if (normalized.length > max)
|
|
478
|
+
throw new Error(`${field} exceeds ${max} characters`);
|
|
479
|
+
return normalized;
|
|
480
|
+
}
|
|
481
|
+
function enumValue(value, values, field) {
|
|
482
|
+
if (typeof value !== "string" || !values.includes(value)) {
|
|
483
|
+
throw new Error(`${field} must be one of ${values.join(", ")}`);
|
|
484
|
+
}
|
|
485
|
+
return value;
|
|
486
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { readSupervisorPlan, readSupervisorPlans } from "./plan-read.js";
|
|
2
|
+
export { SUPERVISOR_PLAN_SCHEMA_VERSION, type SupervisorPlanEvent, type SupervisorPlanEventType, type SupervisorPlanHistory, type SupervisorPlanOutcome, type SupervisorPlanProposal, type SupervisorPlanRecord, type SupervisorPlanRequest, type SupervisorPlanStatus, type SupervisorPlanTemplate, type SupervisorPlanWorkSpec, type SupervisorReplanningPolicy, } from "./plan-types.js";
|
|
1
3
|
export type { SupervisorServiceConfig, SupervisorServiceGoalRuntime, SupervisorServiceGoalState, SupervisorServiceProcessState, SupervisorServiceRuntime, SupervisorServiceStatus, SupervisorServiceStatusRecord, } from "./service.js";
|
|
2
4
|
export { readSupervisorServiceConfig, readSupervisorServiceRuntime, readSupervisorServiceStatus, supervisorServiceLogPath, } from "./service-read.js";
|
|
3
5
|
export { assertSupervisorId, collectSupervisorWork, listSupervisors, readSupervisor, SUPERVISOR_INTENT_SCHEMA_VERSION, type SupervisorAutomationPolicy, type SupervisorIntent, type SupervisorLimits, type SupervisorNextAction, type SupervisorProjection, type SupervisorRecord, type SupervisorState, } from "./state.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/read.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,uBAAuB,EACvB,4BAA4B,EAC5B,0BAA0B,EAC1B,6BAA6B,EAC7B,wBAAwB,EACxB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,gCAAgC,EAChC,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EACL,8BAA8B,EAC9B,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,GAChC,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,uBAAuB,EACvB,4BAA4B,EAC5B,0BAA0B,EAC1B,6BAA6B,EAC7B,wBAAwB,EACxB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,gCAAgC,EAChC,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
export { readSupervisorPlan, readSupervisorPlans } from "./plan-read.js";
|
|
2
|
+
export { SUPERVISOR_PLAN_SCHEMA_VERSION, } from "./plan-types.js";
|
|
1
3
|
export { readSupervisorServiceConfig, readSupervisorServiceRuntime, readSupervisorServiceStatus, supervisorServiceLogPath, } from "./service-read.js";
|
|
2
4
|
export { assertSupervisorId, collectSupervisorWork, listSupervisors, readSupervisor, SUPERVISOR_INTENT_SCHEMA_VERSION, } from "./state.js";
|