harnery 0.17.0 → 0.19.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/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +187 -4
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +76 -4
- package/dist/core/supervisor/index.d.ts +3 -3
- package/dist/core/supervisor/index.d.ts.map +1 -1
- package/dist/core/supervisor/index.js +2 -2
- package/dist/core/supervisor/plan-read.d.ts +2 -1
- package/dist/core/supervisor/plan-read.d.ts.map +1 -1
- package/dist/core/supervisor/plan-read.js +222 -3
- package/dist/core/supervisor/plan-types.d.ts +60 -3
- package/dist/core/supervisor/plan-types.d.ts.map +1 -1
- package/dist/core/supervisor/plan-types.js +2 -0
- package/dist/core/supervisor/planning.d.ts.map +1 -1
- package/dist/core/supervisor/planning.js +804 -56
- package/dist/core/supervisor/read.d.ts +3 -3
- package/dist/core/supervisor/read.d.ts.map +1 -1
- package/dist/core/supervisor/read.js +2 -2
- 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 +211 -33
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +24 -6
- package/dist/core/workflow/types.d.ts +6 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/validate.d.ts +3 -2
- package/dist/core/workflow/validate.d.ts.map +1 -1
- package/dist/core/workflow/validate.js +31 -3
- package/dist/lib/browser/client.d.ts +18 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +39 -0
- package/dist/lib/browser/geometry.d.ts +146 -0
- package/dist/lib/browser/geometry.d.ts.map +1 -0
- package/dist/lib/browser/geometry.js +525 -0
- package/dist/lib/browser/index.d.ts +2 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/target-size.d.ts +65 -0
- package/dist/lib/browser/target-size.d.ts.map +1 -0
- package/dist/lib/browser/target-size.js +108 -0
- package/package.json +7 -1
- package/src/commands/browse.ts +319 -0
- package/src/commands/supervisor.ts +92 -5
- package/src/core/supervisor/index.ts +17 -1
- package/src/core/supervisor/plan-read.ts +292 -2
- package/src/core/supervisor/plan-types.ts +69 -1
- package/src/core/supervisor/planning.ts +1012 -51
- package/src/core/supervisor/read.ts +16 -1
- package/src/core/supervisor/runner.ts +9 -2
- package/src/core/supervisor/service.ts +3 -0
- package/src/core/supervisor/state.ts +272 -31
- package/src/core/workflow/engine.ts +29 -6
- package/src/core/workflow/types.ts +6 -0
- package/src/core/workflow/validate.ts +34 -3
- package/src/lib/browser/client.ts +62 -0
- package/src/lib/browser/geometry.ts +695 -0
- package/src/lib/browser/index.ts +25 -0
- package/src/lib/browser/target-size.ts +180 -0
|
@@ -1,6 +1,6 @@
|
|
|
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
|
+
export { readSupervisorPlan, readSupervisorPlanReviewReceipt, readSupervisorPlans, } from "./plan-read.js";
|
|
2
|
+
export { MAX_SUPERVISOR_PLAN_REVIEWERS, MAX_SUPERVISOR_PLAN_REVISION_ROUNDS, SUPERVISOR_PLAN_SCHEMA_VERSION, type SupervisorPlanEvent, type SupervisorPlanEventType, type SupervisorPlanHistory, type SupervisorPlanMilestone, type SupervisorPlanOutcome, type SupervisorPlanProposal, type SupervisorPlanRecord, type SupervisorPlanRequest, type SupervisorPlanReviewFinding, type SupervisorPlanReviewReceipt, type SupervisorPlanReviewReviewer, type SupervisorPlanReviewRound, type SupervisorPlanReviewStatus, type SupervisorPlanReviewSummary, type SupervisorPlanReviewVerdict, type SupervisorPlanStatus, type SupervisorPlanTemplate, type SupervisorPlanWorkSpec, type SupervisorReplanningPolicy, } from "./plan-types.js";
|
|
3
3
|
export type { SupervisorServiceConfig, SupervisorServiceGoalRuntime, SupervisorServiceGoalState, SupervisorServiceProcessState, SupervisorServiceRuntime, SupervisorServiceStatus, SupervisorServiceStatusRecord, } from "./service.js";
|
|
4
4
|
export { readSupervisorServiceConfig, readSupervisorServiceRuntime, readSupervisorServiceStatus, supervisorServiceLogPath, } from "./service-read.js";
|
|
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";
|
|
5
|
+
export { assertSupervisorId, collectSupervisorWork, listSupervisors, readSupervisor, SUPERVISOR_INTENT_SCHEMA_VERSION, type SupervisorAutomationPolicy, type SupervisorIntent, type SupervisorLimits, type SupervisorMission, type SupervisorNextAction, type SupervisorProjection, type SupervisorRecord, type SupervisorState, } from "./state.js";
|
|
6
6
|
//# sourceMappingURL=read.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/read.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/read.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,+BAA+B,EAC/B,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,EACnC,8BAA8B,EAC9B,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,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,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { readSupervisorPlan, readSupervisorPlans } from "./plan-read.js";
|
|
2
|
-
export { SUPERVISOR_PLAN_SCHEMA_VERSION, } from "./plan-types.js";
|
|
1
|
+
export { readSupervisorPlan, readSupervisorPlanReviewReceipt, readSupervisorPlans, } from "./plan-read.js";
|
|
2
|
+
export { MAX_SUPERVISOR_PLAN_REVIEWERS, MAX_SUPERVISOR_PLAN_REVISION_ROUNDS, SUPERVISOR_PLAN_SCHEMA_VERSION, } from "./plan-types.js";
|
|
3
3
|
export { readSupervisorServiceConfig, readSupervisorServiceRuntime, readSupervisorServiceStatus, supervisorServiceLogPath, } from "./service-read.js";
|
|
4
4
|
export { assertSupervisorId, collectSupervisorWork, listSupervisors, readSupervisor, SUPERVISOR_INTENT_SCHEMA_VERSION, } from "./state.js";
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,31 +6,49 @@ import { assertWorkId, readWorkItem } from "../work/read.js";
|
|
|
6
6
|
import { workflowScriptDigest } from "../workflow/run-state.js";
|
|
7
7
|
import { normalizeWorkflowSpecialists } from "../workflow/specialists.js";
|
|
8
8
|
import { readSupervisorPlans } from "./plan-read.js";
|
|
9
|
-
import { supervisorGraphFingerprint, } from "./plan-types.js";
|
|
9
|
+
import { MAX_SUPERVISOR_PLAN_REVIEWERS, MAX_SUPERVISOR_PLAN_REVISION_ROUNDS, 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;
|
|
33
|
+
const limits = normalizeLimits(input.limits);
|
|
23
34
|
const replanning = input.replanning
|
|
24
|
-
? normalizeReplanning(input.replanning, input.specialists)
|
|
35
|
+
? normalizeReplanning(input.replanning, input.specialists, limits)
|
|
25
36
|
: undefined;
|
|
37
|
+
if (mission &&
|
|
38
|
+
!input.rootWorkId &&
|
|
39
|
+
replanning &&
|
|
40
|
+
replanning.max_replans <= mission.max_milestones) {
|
|
41
|
+
throw new Error("objective-first mission max_replans must exceed max_milestones to reserve completion review");
|
|
42
|
+
}
|
|
26
43
|
const intent = {
|
|
27
44
|
schema_version: SUPERVISOR_INTENT_SCHEMA_VERSION,
|
|
28
45
|
id,
|
|
29
|
-
title: bounded(input.title ?? root
|
|
30
|
-
root_work_id: input.rootWorkId,
|
|
46
|
+
title: bounded(input.title ?? root?.intent.title ?? missionTitle(mission), "supervisor title", MAX_TITLE),
|
|
47
|
+
root_work_id: input.rootWorkId ?? missionRootId(id),
|
|
31
48
|
specialists: normalizeWorkflowSpecialists(input.specialists),
|
|
32
|
-
limits
|
|
49
|
+
limits,
|
|
33
50
|
automation: normalizeAutomation(input.automation),
|
|
51
|
+
...(mission ? { mission } : {}),
|
|
34
52
|
...(replanning ? { replanning } : {}),
|
|
35
53
|
created_at: new Date().toISOString(),
|
|
36
54
|
};
|
|
@@ -136,7 +154,11 @@ function readSupervisorInternal(coordRootRaw, goalId, ignoreLease) {
|
|
|
136
154
|
const coordRoot = resolve(coordRootRaw);
|
|
137
155
|
const intent = readSupervisorIntent(coordRoot, goalId);
|
|
138
156
|
const plans = readSupervisorPlans(coordRoot, intent.id, intent.root_work_id);
|
|
139
|
-
const
|
|
157
|
+
const activeRootExists = workIntentExists(coordRoot, plans.active_root_work_id);
|
|
158
|
+
if (!activeRootExists && (!intent.mission || plans.generation > 0)) {
|
|
159
|
+
throw new Error(`supervisor ${intent.id} root work is missing`);
|
|
160
|
+
}
|
|
161
|
+
const work = activeRootExists ? collectSupervisorWork(coordRoot, plans.active_root_work_id) : [];
|
|
140
162
|
const governed = collectGovernedWork(coordRoot, intent.root_work_id, plans, work);
|
|
141
163
|
return {
|
|
142
164
|
intent,
|
|
@@ -147,8 +169,6 @@ function readSupervisorInternal(coordRootRaw, goalId, ignoreLease) {
|
|
|
147
169
|
}
|
|
148
170
|
function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease) {
|
|
149
171
|
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
172
|
const attemptsUsed = governed.reduce((sum, record) => sum + record.projection.attempts_used, 0);
|
|
153
173
|
const readyWork = work
|
|
154
174
|
.filter((record) => record.projection.state === "ready")
|
|
@@ -181,10 +201,14 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
181
201
|
...cancelled,
|
|
182
202
|
...terminalBlocked,
|
|
183
203
|
]);
|
|
204
|
+
const initialRootAccepted = intent.mission !== undefined &&
|
|
205
|
+
governed.some((record) => record.intent.id === intent.root_work_id && record.projection.state === "succeeded");
|
|
206
|
+
const milestonesCompleted = plans.milestones_completed + (initialRootAccepted ? 1 : 0);
|
|
184
207
|
const base = {
|
|
185
208
|
id: intent.id,
|
|
186
209
|
title: intent.title,
|
|
187
210
|
root_work_id: plans.active_root_work_id,
|
|
211
|
+
root_materialized: root !== undefined,
|
|
188
212
|
work_ids: work.map((record) => record.intent.id),
|
|
189
213
|
ready_work: readyWork,
|
|
190
214
|
resumable_work: resumableWork,
|
|
@@ -196,6 +220,8 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
196
220
|
plan_generation: plans.generation,
|
|
197
221
|
replans_used: plans.plans.length,
|
|
198
222
|
replans_remaining: Math.max(0, (intent.replanning?.max_replans ?? 0) - plans.plans.length),
|
|
223
|
+
milestones_completed: milestonesCompleted,
|
|
224
|
+
milestones_remaining: Math.max(0, (intent.mission?.max_milestones ?? 0) - milestonesCompleted),
|
|
199
225
|
pending_plan_id: plans.latest?.status === "proposed" ? plans.latest.request.id : undefined,
|
|
200
226
|
latest_plan_status: plans.latest?.status,
|
|
201
227
|
governed_work_ids: governed.map((record) => record.intent.id),
|
|
@@ -208,11 +234,11 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
208
234
|
next_action: "wait_for_run",
|
|
209
235
|
};
|
|
210
236
|
}
|
|
211
|
-
if (
|
|
237
|
+
if (intent.mission && plans.completed) {
|
|
212
238
|
return {
|
|
213
239
|
...base,
|
|
214
240
|
state: "succeeded",
|
|
215
|
-
reason: "
|
|
241
|
+
reason: "mission completion was explicitly accepted",
|
|
216
242
|
next_action: "none",
|
|
217
243
|
};
|
|
218
244
|
}
|
|
@@ -240,6 +266,64 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
240
266
|
next_action: "replan",
|
|
241
267
|
};
|
|
242
268
|
}
|
|
269
|
+
const triggerFingerprint = supervisorGraphFingerprint({
|
|
270
|
+
rootWorkId: plans.active_root_work_id,
|
|
271
|
+
generation: plans.generation,
|
|
272
|
+
work,
|
|
273
|
+
});
|
|
274
|
+
const latestHandledSameGraph = plans.latest?.request.trigger_fingerprint === triggerFingerprint &&
|
|
275
|
+
plans.latest.request.prior_root_work_id === plans.active_root_work_id &&
|
|
276
|
+
plans.latest.status === "attention";
|
|
277
|
+
if (latestHandledSameGraph && plans.latest) {
|
|
278
|
+
return {
|
|
279
|
+
...base,
|
|
280
|
+
state: "awaiting_attention",
|
|
281
|
+
reason: plans.latest.reason ?? `plan ${plans.latest.request.id} requires attention`,
|
|
282
|
+
next_action: "none",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (!root && intent.mission) {
|
|
286
|
+
if (!intent.replanning || plans.plans.length >= intent.replanning.max_replans) {
|
|
287
|
+
return {
|
|
288
|
+
...base,
|
|
289
|
+
state: "budget_exhausted",
|
|
290
|
+
reason: "mission exhausted its planning budget before creating an initial milestone",
|
|
291
|
+
next_action: "none",
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
...base,
|
|
296
|
+
state: "ready",
|
|
297
|
+
reason: "mission is ready for its initial bounded milestone plan",
|
|
298
|
+
next_action: "plan_initial",
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (!root)
|
|
302
|
+
throw new Error(`supervisor ${intent.id} root work is missing`);
|
|
303
|
+
if (root.projection.state === "succeeded" && intent.mission) {
|
|
304
|
+
if (!intent.replanning || plans.plans.length >= intent.replanning.max_replans) {
|
|
305
|
+
return {
|
|
306
|
+
...base,
|
|
307
|
+
state: "budget_exhausted",
|
|
308
|
+
reason: "mission exhausted its planning budget at a milestone boundary",
|
|
309
|
+
next_action: "none",
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
...base,
|
|
314
|
+
state: "ready",
|
|
315
|
+
reason: `milestone ${plans.milestones_completed} is accepted and requires mission reassessment`,
|
|
316
|
+
next_action: "plan_milestone",
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (root.projection.state === "succeeded") {
|
|
320
|
+
return {
|
|
321
|
+
...base,
|
|
322
|
+
state: "succeeded",
|
|
323
|
+
reason: "root work was explicitly accepted",
|
|
324
|
+
next_action: "none",
|
|
325
|
+
};
|
|
326
|
+
}
|
|
243
327
|
const resumableDispatchable = intent.automation.resume_approved ? resumableWork : [];
|
|
244
328
|
const attemptDispatchable = [
|
|
245
329
|
...readyWork,
|
|
@@ -318,14 +402,6 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
318
402
|
next_action: "none",
|
|
319
403
|
};
|
|
320
404
|
}
|
|
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
405
|
const canReplan = intent.replanning !== undefined &&
|
|
330
406
|
plans.plans.length < intent.replanning.max_replans &&
|
|
331
407
|
cancelled.length === 0 &&
|
|
@@ -350,14 +426,6 @@ function deriveProjection(coordRoot, intent, work, governed, plans, ignoreLease)
|
|
|
350
426
|
next_action: "none",
|
|
351
427
|
};
|
|
352
428
|
}
|
|
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
429
|
return {
|
|
362
430
|
...base,
|
|
363
431
|
state: "blocked",
|
|
@@ -394,6 +462,19 @@ function validateIntent(intent, goalId) {
|
|
|
394
462
|
}
|
|
395
463
|
bounded(intent.title, "supervisor title", MAX_TITLE);
|
|
396
464
|
assertWorkId(intent.root_work_id);
|
|
465
|
+
if (intent.mission) {
|
|
466
|
+
const normalizedMission = normalizeMission({
|
|
467
|
+
objective: intent.mission.objective,
|
|
468
|
+
acceptance: intent.mission.acceptance,
|
|
469
|
+
maxMilestones: intent.mission.max_milestones,
|
|
470
|
+
});
|
|
471
|
+
if (JSON.stringify(normalizedMission) !== JSON.stringify(intent.mission)) {
|
|
472
|
+
throw new Error(`supervisor intent ${goalId} mission is not canonical`);
|
|
473
|
+
}
|
|
474
|
+
if (!intent.replanning) {
|
|
475
|
+
throw new Error(`supervisor intent ${goalId} mission requires replanning`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
397
478
|
const specialists = normalizeWorkflowSpecialists(intent.specialists);
|
|
398
479
|
if (JSON.stringify(specialists) !== JSON.stringify(intent.specialists)) {
|
|
399
480
|
throw new Error(`supervisor intent ${goalId} specialists are not canonical`);
|
|
@@ -405,7 +486,29 @@ function validateIntent(intent, goalId) {
|
|
|
405
486
|
throw new Error(`supervisor intent ${goalId} automation policy is not canonical`);
|
|
406
487
|
}
|
|
407
488
|
if (intent.replanning)
|
|
408
|
-
validateReplanning(intent.replanning, specialists, goalId);
|
|
489
|
+
validateReplanning(intent.replanning, specialists, intent.limits, goalId);
|
|
490
|
+
}
|
|
491
|
+
function normalizeMission(input) {
|
|
492
|
+
if (!Array.isArray(input.acceptance)) {
|
|
493
|
+
throw new Error("supervisor mission acceptance must be an array");
|
|
494
|
+
}
|
|
495
|
+
if (input.acceptance.length < 1 || input.acceptance.length > MAX_ACCEPTANCE) {
|
|
496
|
+
throw new Error(`supervisor mission acceptance must contain 1 to ${MAX_ACCEPTANCE} criteria`);
|
|
497
|
+
}
|
|
498
|
+
const acceptance = input.acceptance.map((criterion, index) => bounded(criterion, `supervisor mission acceptance[${index}]`, 500));
|
|
499
|
+
if (new Set(acceptance).size !== acceptance.length) {
|
|
500
|
+
throw new Error("supervisor mission acceptance criteria must be unique");
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
objective: bounded(input.objective, "supervisor mission objective", MAX_OBJECTIVE),
|
|
504
|
+
acceptance,
|
|
505
|
+
max_milestones: positive(input.maxMilestones ?? 4, "supervisor mission max_milestones", 20),
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function missionTitle(mission) {
|
|
509
|
+
return mission.objective.length <= MAX_TITLE
|
|
510
|
+
? mission.objective
|
|
511
|
+
: `${mission.objective.slice(0, MAX_TITLE - 1).trimEnd()}…`;
|
|
409
512
|
}
|
|
410
513
|
function normalizeLimits(input) {
|
|
411
514
|
return {
|
|
@@ -424,7 +527,7 @@ function normalizeAutomation(input) {
|
|
|
424
527
|
retry_blocked: boolean(input?.retry_blocked ?? false, "retry_blocked"),
|
|
425
528
|
};
|
|
426
529
|
}
|
|
427
|
-
function normalizeReplanning(input, specialistsInput) {
|
|
530
|
+
function normalizeReplanning(input, specialistsInput, limits) {
|
|
428
531
|
const specialists = normalizeWorkflowSpecialists(specialistsInput);
|
|
429
532
|
const plannerSpecialist = bounded(input.plannerSpecialist, "planner specialist", 100);
|
|
430
533
|
if (!specialists[plannerSpecialist]) {
|
|
@@ -460,6 +563,9 @@ function normalizeReplanning(input, specialistsInput) {
|
|
|
460
563
|
if (maxWorkItemsPerPlan > maxTotalWorkItems) {
|
|
461
564
|
throw new Error("replanning max_work_items_per_plan cannot exceed max_total_work_items");
|
|
462
565
|
}
|
|
566
|
+
const review = input.review
|
|
567
|
+
? normalizeReviewPolicy(input.review, specialists, plannerSpecialist, limits)
|
|
568
|
+
: undefined;
|
|
463
569
|
return {
|
|
464
570
|
planner_specialist: plannerSpecialist,
|
|
465
571
|
auto_apply: boolean(input.autoApply ?? false, "replanning auto_apply"),
|
|
@@ -467,9 +573,39 @@ function normalizeReplanning(input, specialistsInput) {
|
|
|
467
573
|
max_work_items_per_plan: maxWorkItemsPerPlan,
|
|
468
574
|
max_total_work_items: maxTotalWorkItems,
|
|
469
575
|
templates,
|
|
576
|
+
...(review ? { review } : {}),
|
|
470
577
|
};
|
|
471
578
|
}
|
|
472
|
-
function
|
|
579
|
+
function normalizeReviewPolicy(input, specialists, plannerSpecialist, limits) {
|
|
580
|
+
if (!Array.isArray(input.reviewerSpecialists) || input.reviewerSpecialists.length < 1) {
|
|
581
|
+
throw new Error("replanning review requires at least one reviewer specialist");
|
|
582
|
+
}
|
|
583
|
+
if (input.reviewerSpecialists.length > MAX_SUPERVISOR_PLAN_REVIEWERS) {
|
|
584
|
+
throw new Error(`replanning review cannot exceed ${MAX_SUPERVISOR_PLAN_REVIEWERS} reviewer specialists`);
|
|
585
|
+
}
|
|
586
|
+
const reviewerSpecialists = input.reviewerSpecialists.map((specialist, index) => bounded(specialist, `reviewer specialist[${index}]`, 100));
|
|
587
|
+
if (new Set(reviewerSpecialists).size !== reviewerSpecialists.length) {
|
|
588
|
+
throw new Error("replanning reviewer specialists must be unique");
|
|
589
|
+
}
|
|
590
|
+
for (const specialist of reviewerSpecialists) {
|
|
591
|
+
if (!specialists[specialist]) {
|
|
592
|
+
throw new Error(`reviewer specialist ${specialist} is not present in the frozen team`);
|
|
593
|
+
}
|
|
594
|
+
if (specialist === plannerSpecialist) {
|
|
595
|
+
throw new Error("reviewer specialist cannot be the planner specialist");
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const maxRevisionRounds = nonNegative(input.maxRevisionRounds, "replanning review max_revision_rounds", MAX_SUPERVISOR_PLAN_REVISION_ROUNDS);
|
|
599
|
+
const worstCaseAgents = 1 + reviewerSpecialists.length * (maxRevisionRounds + 1) + maxRevisionRounds;
|
|
600
|
+
if (worstCaseAgents > limits.max_agents_per_work) {
|
|
601
|
+
throw new Error(`replanning review worst-case ${worstCaseAgents} agents exceeds max_agents_per_work ${limits.max_agents_per_work}`);
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
reviewer_specialists: reviewerSpecialists,
|
|
605
|
+
max_revision_rounds: maxRevisionRounds,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function validateReplanning(policy, specialists, limits, goalId) {
|
|
473
609
|
if (!specialists[policy.planner_specialist]) {
|
|
474
610
|
throw new Error(`supervisor ${goalId} planner specialist is not in the frozen team`);
|
|
475
611
|
}
|
|
@@ -499,11 +635,41 @@ function validateReplanning(policy, specialists, goalId) {
|
|
|
499
635
|
}
|
|
500
636
|
if (!rootCapable)
|
|
501
637
|
throw new Error(`supervisor ${goalId} has no root-capable replanning template`);
|
|
638
|
+
if (policy.review !== undefined) {
|
|
639
|
+
const review = policy.review;
|
|
640
|
+
if (!Array.isArray(review.reviewer_specialists) ||
|
|
641
|
+
review.reviewer_specialists.length < 1 ||
|
|
642
|
+
review.reviewer_specialists.length > MAX_SUPERVISOR_PLAN_REVIEWERS ||
|
|
643
|
+
!Number.isSafeInteger(review.max_revision_rounds) ||
|
|
644
|
+
review.max_revision_rounds < 0 ||
|
|
645
|
+
review.max_revision_rounds > MAX_SUPERVISOR_PLAN_REVISION_ROUNDS) {
|
|
646
|
+
throw new Error(`supervisor ${goalId} replanning review policy is invalid`);
|
|
647
|
+
}
|
|
648
|
+
if (new Set(review.reviewer_specialists).size !== review.reviewer_specialists.length) {
|
|
649
|
+
throw new Error(`supervisor ${goalId} replanning reviewers are not unique`);
|
|
650
|
+
}
|
|
651
|
+
for (const specialist of review.reviewer_specialists) {
|
|
652
|
+
if (!specialists[specialist]) {
|
|
653
|
+
throw new Error(`supervisor ${goalId} reviewer specialist is not in the frozen team`);
|
|
654
|
+
}
|
|
655
|
+
if (specialist === policy.planner_specialist) {
|
|
656
|
+
throw new Error(`supervisor ${goalId} reviewer specialist cannot be planner`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
const worstCaseAgents = 1 +
|
|
660
|
+
review.reviewer_specialists.length * (review.max_revision_rounds + 1) +
|
|
661
|
+
review.max_revision_rounds;
|
|
662
|
+
if (worstCaseAgents > limits.max_agents_per_work) {
|
|
663
|
+
throw new Error(`supervisor ${goalId} review policy exceeds the agent budget`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
502
666
|
}
|
|
503
667
|
function collectGovernedWork(coordRoot, originalRootWorkId, plans, active) {
|
|
504
668
|
const governed = new Map();
|
|
505
|
-
|
|
506
|
-
|
|
669
|
+
if (workIntentExists(coordRoot, originalRootWorkId)) {
|
|
670
|
+
for (const record of collectSupervisorWork(coordRoot, originalRootWorkId)) {
|
|
671
|
+
governed.set(record.intent.id, record);
|
|
672
|
+
}
|
|
507
673
|
}
|
|
508
674
|
for (const workId of plans.applied_work_ids)
|
|
509
675
|
governed.set(workId, readWorkItem(coordRoot, workId));
|
|
@@ -511,6 +677,12 @@ function collectGovernedWork(coordRoot, originalRootWorkId, plans, active) {
|
|
|
511
677
|
governed.set(record.intent.id, record);
|
|
512
678
|
return [...governed.values()];
|
|
513
679
|
}
|
|
680
|
+
function workIntentExists(coordRoot, workId) {
|
|
681
|
+
return existsSync(join(resolve(coordRoot), ".harnery", "work", workId, "intent.json"));
|
|
682
|
+
}
|
|
683
|
+
function missionRootId(goalId) {
|
|
684
|
+
return `${goalId.slice(0, 87)}-mission-root`;
|
|
685
|
+
}
|
|
514
686
|
function supervisorDir(coordRoot, goalId) {
|
|
515
687
|
assertSupervisorId(goalId);
|
|
516
688
|
return join(resolve(coordRoot), ".harnery", "supervisors", goalId);
|
|
@@ -579,6 +751,12 @@ function positive(value, field, max) {
|
|
|
579
751
|
}
|
|
580
752
|
return value;
|
|
581
753
|
}
|
|
754
|
+
function nonNegative(value, field, max) {
|
|
755
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > max) {
|
|
756
|
+
throw new Error(`${field} must be an integer between 0 and ${max}`);
|
|
757
|
+
}
|
|
758
|
+
return value;
|
|
759
|
+
}
|
|
582
760
|
function boolean(value, field) {
|
|
583
761
|
if (typeof value !== "boolean")
|
|
584
762
|
throw new Error(`supervisor ${field} must be boolean`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAqCH,OAAO,KAAK,EAEV,UAAU,EAEV,SAAS,
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAqCH,OAAO,KAAK,EAEV,UAAU,EAEV,SAAS,EAQV,MAAM,YAAY,CAAC;AAYpB,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM/E;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAOpF;AAED,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAyC1F"}
|
|
@@ -350,6 +350,9 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState) {
|
|
|
350
350
|
const assignment = resolveSpecialistAssignment(specialists, prompt, requestedOpts);
|
|
351
351
|
const agentOpts = assignment.opts;
|
|
352
352
|
const assignmentPrompt = assignment.prompt;
|
|
353
|
+
const dispatchPrompt = agentOpts.schema
|
|
354
|
+
? withSchemaContract(assignmentPrompt, agentOpts.schema)
|
|
355
|
+
: assignmentPrompt;
|
|
353
356
|
let reservedForDispatch = 0;
|
|
354
357
|
let spawnCountClaimed = false;
|
|
355
358
|
const harness = agentOpts.harness ?? defaultHarness;
|
|
@@ -409,7 +412,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState) {
|
|
|
409
412
|
max_attempts: maxAttempts,
|
|
410
413
|
max_turns: agentOpts.maxTurns ?? DEFAULT_MAX_TURNS,
|
|
411
414
|
timeout_ms: agentOpts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
412
|
-
prompt_bytes: Buffer.byteLength(
|
|
415
|
+
prompt_bytes: Buffer.byteLength(dispatchPrompt),
|
|
413
416
|
isolation,
|
|
414
417
|
network_access: networkAccess,
|
|
415
418
|
current_cost_usd: round4(costUsd + reservedCostUsd),
|
|
@@ -529,7 +532,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState) {
|
|
|
529
532
|
effort: agentOpts.effort ?? null,
|
|
530
533
|
});
|
|
531
534
|
log(`[${name}] ${currentStage || "(no stage)"} → ${id} [${harness}] ${label}`);
|
|
532
|
-
let attemptPrompt =
|
|
535
|
+
let attemptPrompt = dispatchPrompt;
|
|
533
536
|
let last = null;
|
|
534
537
|
let agentCostUsd = 0;
|
|
535
538
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
@@ -594,12 +597,16 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState) {
|
|
|
594
597
|
return parsed.value;
|
|
595
598
|
}
|
|
596
599
|
journal("agent.schema_retry", { id, attempt, problems });
|
|
597
|
-
//
|
|
598
|
-
//
|
|
600
|
+
// Keep the original cache identity while making the actual output
|
|
601
|
+
// contract explicit. A full bounded copy of the rejected value lets the
|
|
602
|
+
// child repair omissions instead of reconstructing an unseen object.
|
|
599
603
|
attemptPrompt =
|
|
600
|
-
`${
|
|
604
|
+
`${dispatchPrompt}\n\nYour previous reply failed validation. ` +
|
|
605
|
+
`Return a complete replacement value, not a patch.\n\n` +
|
|
606
|
+
`Previous reply (JSON string): ${JSON.stringify(boundedSchemaReply(last.text))}\n\n` +
|
|
607
|
+
`Validation problems:\n` +
|
|
601
608
|
`${problems.map((p) => ` - ${p}`).join("\n")}\n` +
|
|
602
|
-
`Reply with ONLY the corrected JSON
|
|
609
|
+
`Reply with ONLY the corrected JSON value. No prose, no code fences.`;
|
|
603
610
|
}
|
|
604
611
|
const reason = last?.ok
|
|
605
612
|
? `schema validation failed after ${maxAttempts} attempt(s)`
|
|
@@ -793,6 +800,17 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState) {
|
|
|
793
800
|
throw new WorkflowRunError(err.message, runId, proofPath, err);
|
|
794
801
|
}
|
|
795
802
|
}
|
|
803
|
+
const MAX_SCHEMA_RETRY_REPLY = 16_000;
|
|
804
|
+
function withSchemaContract(prompt, schema) {
|
|
805
|
+
return (`${prompt}\n\nOutput contract:\n` +
|
|
806
|
+
`Return ONLY one JSON value matching this schema. Do not use prose or code fences.\n` +
|
|
807
|
+
`${JSON.stringify(schema)}`);
|
|
808
|
+
}
|
|
809
|
+
function boundedSchemaReply(value) {
|
|
810
|
+
if (value.length <= MAX_SCHEMA_RETRY_REPLY)
|
|
811
|
+
return value;
|
|
812
|
+
return `${value.slice(0, MAX_SCHEMA_RETRY_REPLY)}\n[truncated by Harnery]`;
|
|
813
|
+
}
|
|
796
814
|
/** Stable identity for one agent() call, for the resume cache. The ORIGINAL
|
|
797
815
|
* prompt (never a retry-mutated one) plus everything that changes behavior. */
|
|
798
816
|
function agentCallKey(stage, harness, agentOpts, prompt) {
|
|
@@ -174,6 +174,12 @@ export interface StageSchema {
|
|
|
174
174
|
required?: string[];
|
|
175
175
|
/** type=array */
|
|
176
176
|
items?: StageSchema;
|
|
177
|
+
minItems?: number;
|
|
178
|
+
maxItems?: number;
|
|
179
|
+
/** type=string */
|
|
180
|
+
minLength?: number;
|
|
181
|
+
maxLength?: number;
|
|
182
|
+
pattern?: string;
|
|
177
183
|
/** any type: closed value set (compared with ===) */
|
|
178
184
|
enum?: Array<string | number | boolean>;
|
|
179
185
|
}
|