@osolmaz/pi-workflows 0.10.0 → 0.11.1
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 +32 -14
- package/dist/builtins/autodoc.workflow.d.ts +58 -0
- package/dist/builtins/autodoc.workflow.js +266 -0
- package/dist/builtins/autodoc.workflow.js.map +1 -0
- package/dist/builtins/autoimplement.workflow.d.ts +212 -11
- package/dist/builtins/autoimplement.workflow.js +431 -22
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/builtins/{autodevise.workflow.d.ts → autoplan.workflow.d.ts} +9 -9
- package/dist/builtins/{autodevise.workflow.js → autoplan.workflow.js} +20 -20
- package/dist/builtins/autoplan.workflow.js.map +1 -0
- package/dist/builtins/catalog.js +8 -4
- package/dist/builtins/catalog.js.map +1 -1
- package/dist/builtins/index.d.ts +6 -3
- package/dist/builtins/index.js +5 -2
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/monitor.workflow.d.ts +4 -0
- package/dist/builtins/monitor.workflow.js +134 -9
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/builtins/plan-approval.workflow.d.ts +83 -0
- package/dist/builtins/plan-approval.workflow.js +148 -0
- package/dist/builtins/plan-approval.workflow.js.map +1 -0
- package/dist/builtins/plan-presentation.d.ts +7 -0
- package/dist/builtins/plan-presentation.js +44 -0
- package/dist/builtins/plan-presentation.js.map +1 -0
- package/dist/extension/decision-channels.d.ts +132 -0
- package/dist/extension/decision-channels.js +1082 -0
- package/dist/extension/decision-channels.js.map +1 -0
- package/dist/extension/index.d.ts +1 -0
- package/dist/extension/index.js +426 -11
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/widget.js +26 -2
- package/dist/extension/widget.js.map +1 -1
- package/dist/extension/workflow-tool.d.ts +1 -37
- package/dist/extension/workflow-tool.js +1 -42
- package/dist/extension/workflow-tool.js.map +1 -1
- package/dist/herdr/setup.d.ts +13 -1
- package/dist/herdr/setup.js +349 -36
- package/dist/herdr/setup.js.map +1 -1
- package/dist/host/rpc-bridge.js +4 -21
- package/dist/host/rpc-bridge.js.map +1 -1
- package/dist/render/graph-render.js +33 -8
- package/dist/render/graph-render.js.map +1 -1
- package/dist/viewer/cli.d.ts +1 -0
- package/dist/viewer/cli.js +21 -10
- package/dist/viewer/cli.js.map +1 -1
- package/dist/viewer/render.js +36 -2
- package/dist/viewer/render.js.map +1 -1
- package/dist/workflows/composition.js +15 -1
- package/dist/workflows/composition.js.map +1 -1
- package/dist/workflows/decision-presentation.d.ts +18 -0
- package/dist/workflows/decision-presentation.js +417 -0
- package/dist/workflows/decision-presentation.js.map +1 -0
- package/dist/workflows/engine.d.ts +2 -1
- package/dist/workflows/engine.js +86 -4
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +87 -0
- package/dist/workflows/human-decision.js +583 -0
- package/dist/workflows/human-decision.js.map +1 -0
- package/dist/workflows/index.d.ts +3 -1
- package/dist/workflows/index.js +2 -0
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/schema.js +15 -0
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.js +7 -0
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +72 -0
- package/dist/workflows/tool-input.js +141 -0
- package/dist/workflows/tool-input.js.map +1 -0
- package/dist/workflows/types.d.ts +223 -0
- package/docs/HUMAN_DECISIONS.md +371 -0
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +323 -0
- package/docs/MONITOR.md +17 -6
- package/docs/WORKFLOW_COMPOSITION.md +13 -6
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +296 -0
- package/docs/plans/2026-08-19-human-decision-presentations-plan.md +172 -0
- package/docs/plans/2026-08-19-provider-compatible-workflow-tool-schema-plan.md +45 -0
- package/docs/plans/2026-08-19-workflow-composition-plan.md +25 -17
- package/docs/plans/2026-08-20-autoimplement-blocker-challenge-plan.md +138 -0
- package/docs/plans/2026-08-20-herdr-plugin-sync-plan.md +104 -0
- package/docs/run-bundles.md +23 -2
- package/docs/workflows.md +47 -5
- package/examples/workflows/approved-plan.workflow.ts +58 -0
- package/examples/workflows/autoplan.workflow.ts +1 -0
- package/examples/workflows/human-decision.workflow.ts +62 -0
- package/herdr-plugin.toml +1 -1
- package/package.json +2 -1
- package/schemas/decision-presentation-v1.schema.json +83 -0
- package/schemas/human-decision-accepted-v1.schema.json +44 -0
- package/schemas/human-decision-accepted-v2.schema.json +50 -0
- package/schemas/human-decision-answer-attempt-v1.schema.json +37 -0
- package/schemas/human-decision-cancellation-v1.schema.json +15 -0
- package/schemas/human-decision-continuation-v1.schema.json +16 -0
- package/schemas/human-decision-delivery-v1.schema.json +28 -0
- package/schemas/human-decision-delivery-v2.schema.json +36 -0
- package/schemas/human-decision-receipt-v1.schema.json +33 -0
- package/schemas/human-decision-receipt-v2.schema.json +39 -0
- package/schemas/human-decision-request-v1.schema.json +59 -0
- package/schemas/human-decision-request-v2.schema.json +69 -0
- package/schemas/human-decision-resolution-v1.schema.json +27 -0
- package/schemas/human-decision-resolution-v2.schema.json +27 -0
- package/schemas/human-decision-settlement-v1.schema.json +28 -0
- package/skills/autodoc/SKILL.md +43 -0
- package/skills/autoimplement/SKILL.md +58 -0
- package/skills/autoimplement/agents/openai.yaml +4 -0
- package/skills/autoplan/SKILL.md +25 -0
- package/skills/monitor/SKILL.md +3 -1
- package/skills/pi-workflows/SKILL.md +3 -3
- package/src/builtins/autodoc.workflow.ts +325 -0
- package/src/builtins/autoimplement.workflow.ts +493 -23
- package/src/builtins/{autodevise.workflow.ts → autoplan.workflow.ts} +32 -32
- package/src/builtins/catalog.ts +8 -4
- package/src/builtins/index.ts +25 -6
- package/src/builtins/monitor.workflow.ts +162 -8
- package/src/builtins/plan-approval.workflow.ts +190 -0
- package/src/builtins/plan-presentation.ts +57 -0
- package/src/extension/decision-channels.ts +1533 -0
- package/src/extension/index.ts +499 -9
- package/src/extension/widget.ts +39 -2
- package/src/extension/workflow-tool.ts +5 -81
- package/src/herdr/setup.ts +429 -39
- package/src/host/rpc-bridge.ts +7 -30
- package/src/render/graph-render.ts +40 -3
- package/src/viewer/cli.ts +22 -10
- package/src/viewer/render.ts +56 -3
- package/src/workflows/composition.ts +16 -1
- package/src/workflows/decision-presentation.ts +501 -0
- package/src/workflows/engine.ts +108 -4
- package/src/workflows/human-decision.ts +819 -0
- package/src/workflows/index.ts +69 -0
- package/src/workflows/schema.ts +17 -0
- package/src/workflows/store.ts +8 -0
- package/src/workflows/tool-input.ts +202 -0
- package/src/workflows/types.ts +265 -0
- package/dist/builtins/autodevise.workflow.js.map +0 -1
- package/examples/workflows/autodevise.workflow.ts +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { agent, compute, defineWorkflow } from "../workflows/definition.js";
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type AutoplanInput = {
|
|
5
5
|
problem: string;
|
|
6
6
|
scope?: string;
|
|
7
7
|
constraints?: string[];
|
|
@@ -9,7 +9,7 @@ export type AutodeviseInput = {
|
|
|
9
9
|
newEvidence?: unknown;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
export type
|
|
12
|
+
export type AutoplanReady = {
|
|
13
13
|
status: "ready";
|
|
14
14
|
frame: unknown;
|
|
15
15
|
proposal: unknown;
|
|
@@ -21,7 +21,7 @@ export type AutodeviseReady = {
|
|
|
21
21
|
changed: boolean;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export type
|
|
24
|
+
export type AutoplanBlocked = {
|
|
25
25
|
status: "blocked";
|
|
26
26
|
frame: unknown;
|
|
27
27
|
proposal: unknown;
|
|
@@ -44,18 +44,18 @@ function requireString(value: unknown, label: string): string {
|
|
|
44
44
|
return value.trim();
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function parseInput(value: unknown):
|
|
48
|
-
const input = requireRecord(value, "
|
|
47
|
+
function parseInput(value: unknown): AutoplanInput {
|
|
48
|
+
const input = requireRecord(value, "autoplan input");
|
|
49
49
|
const constraints = input.constraints;
|
|
50
50
|
if (
|
|
51
51
|
constraints !== undefined &&
|
|
52
52
|
(!Array.isArray(constraints) || constraints.some((item) => typeof item !== "string"))
|
|
53
53
|
) {
|
|
54
|
-
throw new Error("
|
|
54
|
+
throw new Error("autoplan constraints must be an array of strings");
|
|
55
55
|
}
|
|
56
56
|
return {
|
|
57
|
-
problem: requireString(input.problem, "
|
|
58
|
-
...(input.scope !== undefined ? { scope: requireString(input.scope, "
|
|
57
|
+
problem: requireString(input.problem, "autoplan problem"),
|
|
58
|
+
...(input.scope !== undefined ? { scope: requireString(input.scope, "autoplan scope") } : {}),
|
|
59
59
|
...(constraints !== undefined ? { constraints: [...constraints] as string[] } : {}),
|
|
60
60
|
...(input.previousPlan !== undefined ? { previousPlan: input.previousPlan } : {}),
|
|
61
61
|
...(input.newEvidence !== undefined ? { newEvidence: input.newEvidence } : {}),
|
|
@@ -63,13 +63,13 @@ function parseInput(value: unknown): AutodeviseInput {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function parseSelection(value: unknown): Record<string, unknown> {
|
|
66
|
-
const selection = requireRecord(value, "
|
|
66
|
+
const selection = requireRecord(value, "autoplan selection");
|
|
67
67
|
if (selection.status !== "ready" && selection.status !== "blocked") {
|
|
68
|
-
throw new Error("
|
|
68
|
+
throw new Error("autoplan selection status must be ready or blocked");
|
|
69
69
|
}
|
|
70
|
-
requireString(selection.selected, "
|
|
71
|
-
requireString(selection.why, "
|
|
72
|
-
if (selection.status === "blocked") requireString(selection.blocker, "
|
|
70
|
+
requireString(selection.selected, "autoplan selected solution");
|
|
71
|
+
requireString(selection.why, "autoplan selection reason");
|
|
72
|
+
if (selection.status === "blocked") requireString(selection.blocker, "autoplan blocker");
|
|
73
73
|
return selection;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -77,12 +77,12 @@ function digest(value: unknown): string {
|
|
|
77
77
|
return `sha256:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
export const
|
|
80
|
+
export const autoplanWorkflow = defineWorkflow({
|
|
81
81
|
source: import.meta.url,
|
|
82
|
-
contractId: "pi-workflows.
|
|
83
|
-
name: "
|
|
82
|
+
contractId: "pi-workflows.autoplan.v1",
|
|
83
|
+
name: "autoplan",
|
|
84
84
|
input: parseInput,
|
|
85
|
-
title: ({ input }) => `
|
|
85
|
+
title: ({ input }) => `autoplan: ${input.problem.slice(0, 60)}`,
|
|
86
86
|
presentationPrompt: [
|
|
87
87
|
"Present the selected practical solution and its implementation plan.",
|
|
88
88
|
"Briefly state how the ideal informed the choice and what was excluded as outside scope.",
|
|
@@ -93,18 +93,18 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
93
93
|
exits: {
|
|
94
94
|
ready: {
|
|
95
95
|
from: "finalize",
|
|
96
|
-
validate: (value: unknown):
|
|
96
|
+
validate: (value: unknown): AutoplanReady => value as AutoplanReady,
|
|
97
97
|
},
|
|
98
98
|
blocked: {
|
|
99
99
|
from: "blocked",
|
|
100
|
-
validate: (value: unknown):
|
|
100
|
+
validate: (value: unknown): AutoplanBlocked => value as AutoplanBlocked,
|
|
101
101
|
},
|
|
102
102
|
},
|
|
103
103
|
nodes: {
|
|
104
104
|
frame: agent({
|
|
105
105
|
statusDetail: "framing the problem",
|
|
106
106
|
prompt: ({ input }) => {
|
|
107
|
-
const request = input as
|
|
107
|
+
const request = input as AutoplanInput;
|
|
108
108
|
return [
|
|
109
109
|
`Frame this problem: ${request.problem}`,
|
|
110
110
|
`Authorized scope: ${request.scope ?? "infer it conservatively from the request and current project"}.`,
|
|
@@ -116,7 +116,7 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
116
116
|
].join("\n");
|
|
117
117
|
},
|
|
118
118
|
expectedOutput: `{ "problem": "concise statement", "success": ["criterion"], "inScope": ["change"], "outOfScope": ["change"], "constraints": ["constraint"], "controlBoundary": "what can change" }`,
|
|
119
|
-
validate: (value) => requireRecord(value, "
|
|
119
|
+
validate: (value) => requireRecord(value, "autoplan frame"),
|
|
120
120
|
}),
|
|
121
121
|
propose: agent({
|
|
122
122
|
statusDetail: "devising a solution",
|
|
@@ -129,7 +129,7 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
129
129
|
`Problem frame: ${JSON.stringify(outputs.frame)}`,
|
|
130
130
|
].join("\n"),
|
|
131
131
|
expectedOutput: `{ "solution": "proposal", "rationale": "why", "parts": ["part"], "tradeoffs": ["trade-off"] }`,
|
|
132
|
-
validate: (value) => requireRecord(value, "
|
|
132
|
+
validate: (value) => requireRecord(value, "autoplan proposal"),
|
|
133
133
|
}),
|
|
134
134
|
ideal: agent({
|
|
135
135
|
statusDetail: "describing the ideal end state",
|
|
@@ -141,10 +141,10 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
141
141
|
"Explain the practical value beyond the proposal.",
|
|
142
142
|
`Problem frame: ${JSON.stringify(outputs.frame)}`,
|
|
143
143
|
`Proposal: ${JSON.stringify(outputs.propose)}`,
|
|
144
|
-
`New evidence: ${JSON.stringify((input as
|
|
144
|
+
`New evidence: ${JSON.stringify((input as AutoplanInput).newEvidence ?? null)}`,
|
|
145
145
|
].join("\n"),
|
|
146
146
|
expectedOutput: `{ "ideal": "ideal end state", "outsideDependencies": ["dependency"], "additionalValue": ["benefit"] }`,
|
|
147
|
-
validate: (value) => requireRecord(value, "
|
|
147
|
+
validate: (value) => requireRecord(value, "autoplan ideal"),
|
|
148
148
|
}),
|
|
149
149
|
choose: agent({
|
|
150
150
|
statusDetail: "choosing the practical solution",
|
|
@@ -177,11 +177,11 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
177
177
|
"Do not implement the plan.",
|
|
178
178
|
`Frame: ${JSON.stringify(outputs.frame)}`,
|
|
179
179
|
`Selection: ${JSON.stringify(outputs.choose)}`,
|
|
180
|
-
`Previous plan: ${JSON.stringify((input as
|
|
181
|
-
`New evidence: ${JSON.stringify((input as
|
|
180
|
+
`Previous plan: ${JSON.stringify((input as AutoplanInput).previousPlan ?? null)}`,
|
|
181
|
+
`New evidence: ${JSON.stringify((input as AutoplanInput).newEvidence ?? null)}`,
|
|
182
182
|
].join("\n"),
|
|
183
183
|
expectedOutput: `{ "summary": "approach", "steps": [{ "change": "change", "where": "location", "verification": "evidence" }], "contracts": ["impact"], "tests": ["test"], "risks": [{ "risk": "risk", "mitigation": "mitigation" }], "boundaries": ["excluded work"] }`,
|
|
184
|
-
validate: (value) => requireRecord(value, "
|
|
184
|
+
validate: (value) => requireRecord(value, "autoplan plan"),
|
|
185
185
|
}),
|
|
186
186
|
blocked: compute({
|
|
187
187
|
run: ({ outputs }) => {
|
|
@@ -192,13 +192,13 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
192
192
|
proposal: outputs.propose,
|
|
193
193
|
ideal: outputs.ideal,
|
|
194
194
|
selection,
|
|
195
|
-
reason: requireString(selection.blocker, "
|
|
196
|
-
} satisfies
|
|
195
|
+
reason: requireString(selection.blocker, "autoplan blocker"),
|
|
196
|
+
} satisfies AutoplanBlocked;
|
|
197
197
|
},
|
|
198
198
|
}),
|
|
199
199
|
finalize: compute({
|
|
200
200
|
run: ({ outputs, input }) => {
|
|
201
|
-
const request = input as
|
|
201
|
+
const request = input as AutoplanInput;
|
|
202
202
|
const planDigest = digest(outputs.plan);
|
|
203
203
|
const previousPlanDigest =
|
|
204
204
|
request.previousPlan === undefined ? undefined : digest(request.previousPlan);
|
|
@@ -212,7 +212,7 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
212
212
|
planDigest,
|
|
213
213
|
...(previousPlanDigest !== undefined ? { previousPlanDigest } : {}),
|
|
214
214
|
changed: previousPlanDigest === undefined || previousPlanDigest !== planDigest,
|
|
215
|
-
} satisfies
|
|
215
|
+
} satisfies AutoplanReady;
|
|
216
216
|
},
|
|
217
217
|
}),
|
|
218
218
|
},
|
|
@@ -228,4 +228,4 @@ export const autodeviseWorkflow = defineWorkflow({
|
|
|
228
228
|
],
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
-
export default
|
|
231
|
+
export default autoplanWorkflow;
|
package/src/builtins/catalog.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { BuiltinWorkflowCatalog } from "../workflows/catalog.js";
|
|
2
|
-
import
|
|
2
|
+
import autodocWorkflow from "./autodoc.workflow.js";
|
|
3
3
|
import autoimplementWorkflow from "./autoimplement.workflow.js";
|
|
4
|
+
import autoplanWorkflow from "./autoplan.workflow.js";
|
|
4
5
|
import monitorWorkflow from "./monitor.workflow.js";
|
|
6
|
+
import planApprovalWorkflow from "./plan-approval.workflow.js";
|
|
5
7
|
|
|
6
8
|
export const builtinWorkflowCatalog = new BuiltinWorkflowCatalog([
|
|
7
|
-
{ id: "
|
|
8
|
-
{ id: "
|
|
9
|
+
{ id: "autoplan", revision: "1", definition: autoplanWorkflow },
|
|
10
|
+
{ id: "autodoc", revision: "1", definition: autodocWorkflow },
|
|
11
|
+
{ id: "autoimplement", revision: "4", definition: autoimplementWorkflow },
|
|
12
|
+
{ id: "plan-approval", revision: "2", definition: planApprovalWorkflow },
|
|
9
13
|
{
|
|
10
14
|
id: "monitor",
|
|
11
|
-
revision: "
|
|
15
|
+
revision: "7",
|
|
12
16
|
definition: monitorWorkflow,
|
|
13
17
|
legacySources: [
|
|
14
18
|
{
|
package/src/builtins/index.ts
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
export {
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
type
|
|
5
|
-
type
|
|
6
|
-
} from "./
|
|
2
|
+
autoplanWorkflow,
|
|
3
|
+
type AutoplanBlocked,
|
|
4
|
+
type AutoplanInput,
|
|
5
|
+
type AutoplanReady,
|
|
6
|
+
} from "./autoplan.workflow.js";
|
|
7
|
+
export {
|
|
8
|
+
autodocWorkflow,
|
|
9
|
+
type AutodocBlocked,
|
|
10
|
+
type AutodocInput,
|
|
11
|
+
type DocumentedPlan,
|
|
12
|
+
} from "./autodoc.workflow.js";
|
|
7
13
|
export {
|
|
8
14
|
autoimplementWorkflow,
|
|
9
15
|
type AutoimplementBlocked,
|
|
10
16
|
type AutoimplementCompleted,
|
|
11
17
|
type AutoimplementInput,
|
|
18
|
+
type ExistingPlanDiscovery,
|
|
12
19
|
} from "./autoimplement.workflow.js";
|
|
13
|
-
export {
|
|
20
|
+
export {
|
|
21
|
+
default as monitorWorkflow,
|
|
22
|
+
type MonitorInput,
|
|
23
|
+
type MonitorRepairPolicy,
|
|
24
|
+
} from "./monitor.workflow.js";
|
|
25
|
+
export { presentPlan } from "./plan-presentation.js";
|
|
26
|
+
export {
|
|
27
|
+
planApprovalWorkflow,
|
|
28
|
+
type PlanApprovalContinue,
|
|
29
|
+
type PlanApprovalInput,
|
|
30
|
+
type PlanApprovalReplan,
|
|
31
|
+
type PlanApprovalStop,
|
|
32
|
+
} from "./plan-approval.workflow.js";
|
|
@@ -20,8 +20,10 @@ import type {
|
|
|
20
20
|
WorkflowProgressData,
|
|
21
21
|
} from "../workflows/types.js";
|
|
22
22
|
import { validateProgressData } from "../workflows/updates.js";
|
|
23
|
-
import
|
|
23
|
+
import autodocWorkflow, { type AutodocInput } from "./autodoc.workflow.js";
|
|
24
24
|
import autoimplementWorkflow, { type AutoimplementInput } from "./autoimplement.workflow.js";
|
|
25
|
+
import autoplanWorkflow from "./autoplan.workflow.js";
|
|
26
|
+
import planApprovalWorkflow, { type PlanApprovalInput } from "./plan-approval.workflow.js";
|
|
25
27
|
|
|
26
28
|
const MIN_INTERVAL_MINUTES = 1;
|
|
27
29
|
const MAX_INTERVAL_MINUTES = 24 * 60;
|
|
@@ -45,6 +47,10 @@ export type MonitorRepairPolicy = {
|
|
|
45
47
|
repository?: string;
|
|
46
48
|
baseBranch?: string;
|
|
47
49
|
merge?: boolean;
|
|
50
|
+
approval?: {
|
|
51
|
+
audience: string;
|
|
52
|
+
maxReplans: number;
|
|
53
|
+
};
|
|
48
54
|
};
|
|
49
55
|
|
|
50
56
|
export type MonitorInput = {
|
|
@@ -163,6 +169,22 @@ export function prepareMonitorInput(input: unknown): MonitorConfig {
|
|
|
163
169
|
if (raw.merge !== undefined && typeof raw.merge !== "boolean") {
|
|
164
170
|
throw new Error("repair merge must be a boolean");
|
|
165
171
|
}
|
|
172
|
+
let approval: MonitorRepairPolicy["approval"];
|
|
173
|
+
if (raw.approval !== undefined) {
|
|
174
|
+
const value = requireRecord(raw.approval, "repair approval");
|
|
175
|
+
const maxReplans = value.maxReplans ?? 3;
|
|
176
|
+
if (
|
|
177
|
+
!Number.isInteger(maxReplans) ||
|
|
178
|
+
(maxReplans as number) < 1 ||
|
|
179
|
+
(maxReplans as number) > 20
|
|
180
|
+
) {
|
|
181
|
+
throw new Error("repair approval maxReplans must be from 1 through 20");
|
|
182
|
+
}
|
|
183
|
+
approval = {
|
|
184
|
+
audience: requireBoundedString(value.audience, "repair approval audience", 200),
|
|
185
|
+
maxReplans: maxReplans as number,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
166
188
|
repair = {
|
|
167
189
|
authorized: true,
|
|
168
190
|
...(raw.scope !== undefined
|
|
@@ -176,6 +198,7 @@ export function prepareMonitorInput(input: unknown): MonitorConfig {
|
|
|
176
198
|
? { baseBranch: requireBoundedString(raw.baseBranch, "repair base branch", 256) }
|
|
177
199
|
: {}),
|
|
178
200
|
...(raw.merge !== undefined ? { merge: raw.merge !== false } : {}),
|
|
201
|
+
...(approval !== undefined ? { approval } : {}),
|
|
179
202
|
};
|
|
180
203
|
}
|
|
181
204
|
return {
|
|
@@ -297,9 +320,56 @@ function repeatedRepairWithoutProgress(context: WorkflowNodeContext): boolean {
|
|
|
297
320
|
return false;
|
|
298
321
|
}
|
|
299
322
|
|
|
323
|
+
function currentRepairPlan(outputs: Record<string, unknown>): {
|
|
324
|
+
plan: unknown;
|
|
325
|
+
planDigest: string;
|
|
326
|
+
documents: string[];
|
|
327
|
+
} {
|
|
328
|
+
const documentation = outputs.documentation as
|
|
329
|
+
| {
|
|
330
|
+
exit?: string;
|
|
331
|
+
output?: {
|
|
332
|
+
plan?: unknown;
|
|
333
|
+
planDigest?: string;
|
|
334
|
+
documentation?: { files?: string[] };
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
| undefined;
|
|
338
|
+
if (
|
|
339
|
+
documentation?.exit === "ready" &&
|
|
340
|
+
documentation.output?.plan !== undefined &&
|
|
341
|
+
typeof documentation.output.planDigest === "string"
|
|
342
|
+
) {
|
|
343
|
+
return {
|
|
344
|
+
plan: documentation.output.plan,
|
|
345
|
+
planDigest: documentation.output.planDigest,
|
|
346
|
+
documents: documentation.output.documentation?.files ?? [],
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const design = includedResult(autoplanWorkflow, outputs.initialDesign);
|
|
350
|
+
if (design.exit !== "ready") throw new Error("monitor design did not return a ready plan");
|
|
351
|
+
return { plan: design.output.plan, planDigest: design.output.planDigest, documents: [] };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function latestReplanInstructions(outputs: Record<string, unknown>): string | undefined {
|
|
355
|
+
const approval = outputs.approval as
|
|
356
|
+
| { exit?: string; output?: { instructions?: unknown } }
|
|
357
|
+
| undefined;
|
|
358
|
+
return approval?.exit === "replan" && typeof approval.output?.instructions === "string"
|
|
359
|
+
? approval.output.instructions
|
|
360
|
+
: undefined;
|
|
361
|
+
}
|
|
362
|
+
|
|
300
363
|
function repairBlockedReason(outputs: Record<string, unknown>): string {
|
|
301
364
|
const guard = outputs.repairGuard as { reason?: string; route?: string } | undefined;
|
|
302
365
|
if (guard?.route === "blocked" && guard.reason !== undefined) return guard.reason;
|
|
366
|
+
const replan = outputs.replanGuard as { reason?: string; route?: string } | undefined;
|
|
367
|
+
if (replan?.route === "blocked" && replan.reason !== undefined) return replan.reason;
|
|
368
|
+
const approval = outputs.approval as { exit?: string; output?: { status?: string } } | undefined;
|
|
369
|
+
if (approval?.exit === "stop") return "The operator stopped the proposed repair.";
|
|
370
|
+
const documentation = outputs.documentation as
|
|
371
|
+
| { exit?: string; output?: { reason?: string } }
|
|
372
|
+
| undefined;
|
|
303
373
|
const implementation = outputs.implementation as
|
|
304
374
|
| { exit?: string; output?: { reason?: string } }
|
|
305
375
|
| undefined;
|
|
@@ -308,6 +378,7 @@ function repairBlockedReason(outputs: Record<string, unknown>): string {
|
|
|
308
378
|
| undefined;
|
|
309
379
|
return (
|
|
310
380
|
implementation?.output?.reason ??
|
|
381
|
+
documentation?.output?.reason ??
|
|
311
382
|
design?.output?.reason ??
|
|
312
383
|
"The repair did not produce new verified progress."
|
|
313
384
|
);
|
|
@@ -357,19 +428,66 @@ const monitorWorkflow: WorkflowDefinition = defineWorkflow({
|
|
|
357
428
|
maxSteps: 200_000,
|
|
358
429
|
includes: {
|
|
359
430
|
initialDesign: includeWorkflow({
|
|
360
|
-
workflow: "
|
|
361
|
-
contract:
|
|
431
|
+
workflow: "autoplan",
|
|
432
|
+
contract: autoplanWorkflow,
|
|
362
433
|
input: ({ outputs }) => {
|
|
363
434
|
const config = configFrom(outputs);
|
|
364
435
|
const repair = (outputs.check as MonitorCheck).repair;
|
|
365
436
|
if (repair === undefined) throw new Error("monitor repair details are missing");
|
|
437
|
+
const prior = outputs.initialDesign as
|
|
438
|
+
| { exit?: string; output?: { plan?: unknown } }
|
|
439
|
+
| undefined;
|
|
440
|
+
const instructions = latestReplanInstructions(outputs);
|
|
366
441
|
return {
|
|
367
442
|
problem: repair.problem,
|
|
368
443
|
...(config.repair?.scope !== undefined ? { scope: config.repair.scope } : {}),
|
|
369
444
|
...(config.repair?.constraints !== undefined
|
|
370
445
|
? { constraints: config.repair.constraints }
|
|
371
446
|
: {}),
|
|
372
|
-
|
|
447
|
+
...(prior?.exit === "ready" && prior.output?.plan !== undefined
|
|
448
|
+
? { previousPlan: prior.output.plan }
|
|
449
|
+
: {}),
|
|
450
|
+
newEvidence:
|
|
451
|
+
instructions === undefined
|
|
452
|
+
? repair.evidence
|
|
453
|
+
: { observedEvidence: repair.evidence, operatorInstructions: instructions },
|
|
454
|
+
};
|
|
455
|
+
},
|
|
456
|
+
}),
|
|
457
|
+
documentation: includeWorkflow(autodocWorkflow, {
|
|
458
|
+
input: ({ outputs }): AutodocInput => {
|
|
459
|
+
const config = configFrom(outputs);
|
|
460
|
+
const repair = (outputs.check as MonitorCheck).repair;
|
|
461
|
+
const design = includedResult(autoplanWorkflow, outputs.initialDesign);
|
|
462
|
+
if (design.exit !== "ready") throw new Error("monitor design did not return a ready plan");
|
|
463
|
+
if (repair === undefined) throw new Error("monitor repair details are missing");
|
|
464
|
+
return {
|
|
465
|
+
task: repair.problem,
|
|
466
|
+
plan: design.output.plan,
|
|
467
|
+
...(config.repair?.repository !== undefined
|
|
468
|
+
? { repository: config.repair.repository }
|
|
469
|
+
: {}),
|
|
470
|
+
evidence: latestReplanInstructions(outputs) ?? repair.evidence,
|
|
471
|
+
};
|
|
472
|
+
},
|
|
473
|
+
}),
|
|
474
|
+
approval: includeWorkflow(planApprovalWorkflow, {
|
|
475
|
+
input: ({ outputs, state }): PlanApprovalInput => {
|
|
476
|
+
const config = configFrom(outputs);
|
|
477
|
+
const repair = (outputs.check as MonitorCheck).repair;
|
|
478
|
+
const approval = config.repair?.approval;
|
|
479
|
+
if (repair === undefined || approval === undefined) {
|
|
480
|
+
throw new Error("monitor approval was entered without an approval policy");
|
|
481
|
+
}
|
|
482
|
+
const current = currentRepairPlan(outputs);
|
|
483
|
+
const revision =
|
|
484
|
+
state.steps.filter((step) => step.nodeId === "approval/approve").length + 1;
|
|
485
|
+
return {
|
|
486
|
+
task: repair.problem,
|
|
487
|
+
plan: current.plan,
|
|
488
|
+
planDigest: current.planDigest,
|
|
489
|
+
audience: approval.audience,
|
|
490
|
+
revision,
|
|
373
491
|
};
|
|
374
492
|
},
|
|
375
493
|
}),
|
|
@@ -379,12 +497,16 @@ const monitorWorkflow: WorkflowDefinition = defineWorkflow({
|
|
|
379
497
|
input: ({ outputs }) => {
|
|
380
498
|
const config = configFrom(outputs);
|
|
381
499
|
const repair = (outputs.check as MonitorCheck).repair;
|
|
382
|
-
const
|
|
383
|
-
if (design.exit !== "ready") throw new Error("monitor design did not return a ready plan");
|
|
500
|
+
const documented = currentRepairPlan(outputs);
|
|
384
501
|
if (repair === undefined) throw new Error("monitor repair details are missing");
|
|
385
502
|
const request: AutoimplementInput = {
|
|
386
503
|
task: repair.problem,
|
|
387
|
-
plan:
|
|
504
|
+
plan: documented.plan,
|
|
505
|
+
documentation: {
|
|
506
|
+
status: "current",
|
|
507
|
+
planDigest: documented.planDigest,
|
|
508
|
+
documents: documented.documents,
|
|
509
|
+
},
|
|
388
510
|
...(config.repair?.scope !== undefined ? { scope: config.repair.scope } : {}),
|
|
389
511
|
...(config.repair?.constraints !== undefined
|
|
390
512
|
? { constraints: config.repair.constraints }
|
|
@@ -481,6 +603,25 @@ const monitorWorkflow: WorkflowDefinition = defineWorkflow({
|
|
|
481
603
|
}
|
|
482
604
|
: { route: "repair", reason: "The issue is new or has changed evidence." },
|
|
483
605
|
}),
|
|
606
|
+
approvalRoute: compute({
|
|
607
|
+
run: ({ outputs }) => ({
|
|
608
|
+
route: configFrom(outputs).repair?.approval === undefined ? "implement" : "approve",
|
|
609
|
+
}),
|
|
610
|
+
}),
|
|
611
|
+
replanGuard: compute({
|
|
612
|
+
run: (context) => {
|
|
613
|
+
const limit = configFrom(context.outputs).repair?.approval?.maxReplans ?? 3;
|
|
614
|
+
const replans = context.state.steps.filter(
|
|
615
|
+
(step) => step.nodeId === "approval/replan",
|
|
616
|
+
).length;
|
|
617
|
+
return replans > limit
|
|
618
|
+
? {
|
|
619
|
+
route: "blocked",
|
|
620
|
+
reason: `Monitor repair reached the ${limit}-replan safety limit.`,
|
|
621
|
+
}
|
|
622
|
+
: { route: "design", replans, limit };
|
|
623
|
+
},
|
|
624
|
+
}),
|
|
484
625
|
repairBlocked: compute({
|
|
485
626
|
run: (context) => ({
|
|
486
627
|
reason: repairBlockedReason(context.outputs),
|
|
@@ -568,8 +709,21 @@ const monitorWorkflow: WorkflowDefinition = defineWorkflow({
|
|
|
568
709
|
from: "repairGuard",
|
|
569
710
|
switch: { on: "$.route", cases: { repair: "initialDesign", blocked: "repairBlocked" } },
|
|
570
711
|
},
|
|
571
|
-
{ from: "initialDesign.ready", to: "
|
|
712
|
+
{ from: "initialDesign.ready", to: "documentation" },
|
|
572
713
|
{ from: "initialDesign.blocked", to: "repairBlocked" },
|
|
714
|
+
{ from: "documentation.ready", to: "approvalRoute" },
|
|
715
|
+
{ from: "documentation.blocked", to: "repairBlocked" },
|
|
716
|
+
{
|
|
717
|
+
from: "approvalRoute",
|
|
718
|
+
switch: { on: "$.route", cases: { approve: "approval", implement: "implementation" } },
|
|
719
|
+
},
|
|
720
|
+
{ from: "approval.continue", to: "implementation" },
|
|
721
|
+
{ from: "approval.stop", to: "repairBlocked" },
|
|
722
|
+
{ from: "approval.replan", to: "replanGuard" },
|
|
723
|
+
{
|
|
724
|
+
from: "replanGuard",
|
|
725
|
+
switch: { on: "$.route", cases: { design: "initialDesign", blocked: "repairBlocked" } },
|
|
726
|
+
},
|
|
573
727
|
{ from: "implementation.completed", to: "check" },
|
|
574
728
|
{ from: "implementation.blocked", to: "repairBlocked" },
|
|
575
729
|
{ from: "repairBlocked", to: "repairReport" },
|