@osolmaz/pi-workflows 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +23 -12
  2. package/dist/builtins/autodoc.workflow.d.ts +58 -0
  3. package/dist/builtins/autodoc.workflow.js +266 -0
  4. package/dist/builtins/autodoc.workflow.js.map +1 -0
  5. package/dist/builtins/autoimplement.workflow.d.ts +210 -11
  6. package/dist/builtins/autoimplement.workflow.js +245 -13
  7. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  8. package/dist/builtins/{autodevise.workflow.d.ts → autoplan.workflow.d.ts} +9 -9
  9. package/dist/builtins/{autodevise.workflow.js → autoplan.workflow.js} +20 -20
  10. package/dist/builtins/autoplan.workflow.js.map +1 -0
  11. package/dist/builtins/catalog.js +8 -4
  12. package/dist/builtins/catalog.js.map +1 -1
  13. package/dist/builtins/index.d.ts +6 -3
  14. package/dist/builtins/index.js +5 -2
  15. package/dist/builtins/index.js.map +1 -1
  16. package/dist/builtins/monitor.workflow.d.ts +4 -0
  17. package/dist/builtins/monitor.workflow.js +134 -9
  18. package/dist/builtins/monitor.workflow.js.map +1 -1
  19. package/dist/builtins/plan-approval.workflow.d.ts +83 -0
  20. package/dist/builtins/plan-approval.workflow.js +148 -0
  21. package/dist/builtins/plan-approval.workflow.js.map +1 -0
  22. package/dist/builtins/plan-presentation.d.ts +7 -0
  23. package/dist/builtins/plan-presentation.js +44 -0
  24. package/dist/builtins/plan-presentation.js.map +1 -0
  25. package/dist/extension/decision-channels.d.ts +132 -0
  26. package/dist/extension/decision-channels.js +1082 -0
  27. package/dist/extension/decision-channels.js.map +1 -0
  28. package/dist/extension/index.d.ts +1 -0
  29. package/dist/extension/index.js +426 -11
  30. package/dist/extension/index.js.map +1 -1
  31. package/dist/extension/widget.js +26 -2
  32. package/dist/extension/widget.js.map +1 -1
  33. package/dist/extension/workflow-tool.d.ts +1 -37
  34. package/dist/extension/workflow-tool.js +1 -42
  35. package/dist/extension/workflow-tool.js.map +1 -1
  36. package/dist/host/rpc-bridge.js +4 -21
  37. package/dist/host/rpc-bridge.js.map +1 -1
  38. package/dist/render/graph-render.js +33 -8
  39. package/dist/render/graph-render.js.map +1 -1
  40. package/dist/viewer/render.js +36 -2
  41. package/dist/viewer/render.js.map +1 -1
  42. package/dist/workflows/composition.js +15 -1
  43. package/dist/workflows/composition.js.map +1 -1
  44. package/dist/workflows/decision-presentation.d.ts +18 -0
  45. package/dist/workflows/decision-presentation.js +417 -0
  46. package/dist/workflows/decision-presentation.js.map +1 -0
  47. package/dist/workflows/engine.d.ts +2 -1
  48. package/dist/workflows/engine.js +86 -4
  49. package/dist/workflows/engine.js.map +1 -1
  50. package/dist/workflows/human-decision.d.ts +87 -0
  51. package/dist/workflows/human-decision.js +583 -0
  52. package/dist/workflows/human-decision.js.map +1 -0
  53. package/dist/workflows/index.d.ts +3 -1
  54. package/dist/workflows/index.js +2 -0
  55. package/dist/workflows/index.js.map +1 -1
  56. package/dist/workflows/schema.js +15 -0
  57. package/dist/workflows/schema.js.map +1 -1
  58. package/dist/workflows/store.js +7 -0
  59. package/dist/workflows/store.js.map +1 -1
  60. package/dist/workflows/tool-input.d.ts +72 -0
  61. package/dist/workflows/tool-input.js +141 -0
  62. package/dist/workflows/tool-input.js.map +1 -0
  63. package/dist/workflows/types.d.ts +223 -0
  64. package/docs/HUMAN_DECISIONS.md +371 -0
  65. package/docs/HUMAN_DECISION_PRESENTATIONS.md +323 -0
  66. package/docs/MONITOR.md +17 -6
  67. package/docs/WORKFLOW_COMPOSITION.md +13 -6
  68. package/docs/plans/2026-08-19-human-decision-gates-plan.md +296 -0
  69. package/docs/plans/2026-08-19-human-decision-presentations-plan.md +172 -0
  70. package/docs/plans/2026-08-19-provider-compatible-workflow-tool-schema-plan.md +45 -0
  71. package/docs/plans/2026-08-19-workflow-composition-plan.md +25 -17
  72. package/docs/run-bundles.md +23 -2
  73. package/docs/workflows.md +43 -5
  74. package/examples/workflows/approved-plan.workflow.ts +58 -0
  75. package/examples/workflows/autoplan.workflow.ts +1 -0
  76. package/examples/workflows/human-decision.workflow.ts +62 -0
  77. package/herdr-plugin.toml +1 -1
  78. package/package.json +2 -1
  79. package/schemas/decision-presentation-v1.schema.json +83 -0
  80. package/schemas/human-decision-accepted-v1.schema.json +44 -0
  81. package/schemas/human-decision-accepted-v2.schema.json +50 -0
  82. package/schemas/human-decision-answer-attempt-v1.schema.json +37 -0
  83. package/schemas/human-decision-cancellation-v1.schema.json +15 -0
  84. package/schemas/human-decision-continuation-v1.schema.json +16 -0
  85. package/schemas/human-decision-delivery-v1.schema.json +28 -0
  86. package/schemas/human-decision-delivery-v2.schema.json +36 -0
  87. package/schemas/human-decision-receipt-v1.schema.json +33 -0
  88. package/schemas/human-decision-receipt-v2.schema.json +39 -0
  89. package/schemas/human-decision-request-v1.schema.json +59 -0
  90. package/schemas/human-decision-request-v2.schema.json +69 -0
  91. package/schemas/human-decision-resolution-v1.schema.json +27 -0
  92. package/schemas/human-decision-resolution-v2.schema.json +27 -0
  93. package/schemas/human-decision-settlement-v1.schema.json +28 -0
  94. package/skills/autodoc/SKILL.md +43 -0
  95. package/skills/autoimplement/SKILL.md +58 -0
  96. package/skills/autoimplement/agents/openai.yaml +4 -0
  97. package/skills/autoplan/SKILL.md +25 -0
  98. package/skills/monitor/SKILL.md +3 -1
  99. package/skills/pi-workflows/SKILL.md +3 -3
  100. package/src/builtins/autodoc.workflow.ts +325 -0
  101. package/src/builtins/autoimplement.workflow.ts +281 -14
  102. package/src/builtins/{autodevise.workflow.ts → autoplan.workflow.ts} +32 -32
  103. package/src/builtins/catalog.ts +8 -4
  104. package/src/builtins/index.ts +25 -6
  105. package/src/builtins/monitor.workflow.ts +162 -8
  106. package/src/builtins/plan-approval.workflow.ts +190 -0
  107. package/src/builtins/plan-presentation.ts +57 -0
  108. package/src/extension/decision-channels.ts +1533 -0
  109. package/src/extension/index.ts +499 -9
  110. package/src/extension/widget.ts +39 -2
  111. package/src/extension/workflow-tool.ts +5 -81
  112. package/src/host/rpc-bridge.ts +7 -30
  113. package/src/render/graph-render.ts +40 -3
  114. package/src/viewer/render.ts +56 -3
  115. package/src/workflows/composition.ts +16 -1
  116. package/src/workflows/decision-presentation.ts +501 -0
  117. package/src/workflows/engine.ts +108 -4
  118. package/src/workflows/human-decision.ts +819 -0
  119. package/src/workflows/index.ts +69 -0
  120. package/src/workflows/schema.ts +17 -0
  121. package/src/workflows/store.ts +8 -0
  122. package/src/workflows/tool-input.ts +202 -0
  123. package/src/workflows/types.ts +265 -0
  124. package/dist/builtins/autodevise.workflow.js.map +0 -1
  125. package/examples/workflows/autodevise.workflow.ts +0 -1
@@ -1,4 +1,7 @@
1
- import { type AutodeviseInput } from "./autodevise.workflow.js";
1
+ import type { WorkflowNodeContext } from "../workflows/types.js";
2
+ import { type AutodocInput } from "./autodoc.workflow.js";
3
+ import { type AutoplanInput } from "./autoplan.workflow.js";
4
+ import { type PlanApprovalInput } from "./plan-approval.workflow.js";
2
5
  export type AutoimplementInput = {
3
6
  task: string;
4
7
  plan?: unknown;
@@ -7,6 +10,24 @@ export type AutoimplementInput = {
7
10
  repository?: string;
8
11
  baseBranch?: string;
9
12
  merge?: boolean;
13
+ documents?: string[];
14
+ documentation?: {
15
+ status: "current";
16
+ planDigest: string;
17
+ documents: string[];
18
+ };
19
+ approval?: {
20
+ audience: string;
21
+ maxReplans: number;
22
+ };
23
+ };
24
+ export type ExistingPlanDiscovery = {
25
+ route: "found" | "blocked";
26
+ plan?: unknown;
27
+ documentation?: "current" | "missing" | "stale";
28
+ documents: string[];
29
+ reason: string;
30
+ evidence: unknown;
10
31
  };
11
32
  type ReviewFinding = {
12
33
  severity: "P0" | "P1" | "P2" | "lower";
@@ -48,14 +69,101 @@ export declare const autoimplementWorkflow: import("../workflows/types.js").Work
48
69
  readonly validate: (value: unknown) => AutoimplementBlocked;
49
70
  };
50
71
  }, {
51
- readonly redesign: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<AutodeviseInput, {
72
+ readonly documentation: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<AutodocInput, {
52
73
  readonly ready: {
53
74
  readonly from: "finalize";
54
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseReady;
75
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").DocumentedPlan;
55
76
  };
56
77
  readonly blocked: {
57
78
  readonly from: "blocked";
58
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseBlocked;
79
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").AutodocBlocked;
80
+ };
81
+ }, import("../workflows/types.js").WorkflowIncludeMap> & {
82
+ nodes: {
83
+ readonly prepare: import("../workflows/types.js").ComputeNodeDefinition;
84
+ readonly locatePlan: import("../workflows/types.js").AgentNodeDefinition;
85
+ readonly inspectDocumentation: import("../workflows/types.js").AgentNodeDefinition;
86
+ readonly updateDocumentation: import("../workflows/types.js").AgentNodeDefinition;
87
+ readonly verifyDocumentation: import("../workflows/types.js").AgentNodeDefinition;
88
+ readonly finalize: import("../workflows/types.js").ComputeNodeDefinition;
89
+ readonly blocked: import("../workflows/types.js").ComputeNodeDefinition;
90
+ };
91
+ includes?: import("../workflows/types.js").WorkflowIncludeMap;
92
+ exits?: {
93
+ readonly ready: {
94
+ readonly from: "finalize";
95
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").DocumentedPlan;
96
+ };
97
+ readonly blocked: {
98
+ readonly from: "blocked";
99
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").AutodocBlocked;
100
+ };
101
+ };
102
+ }>;
103
+ readonly approval: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<PlanApprovalInput, {
104
+ readonly continue: {
105
+ readonly from: "continued";
106
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalContinue;
107
+ };
108
+ readonly stop: {
109
+ readonly from: "stopped";
110
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalStop;
111
+ };
112
+ readonly replan: {
113
+ readonly from: "replan";
114
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalReplan;
115
+ };
116
+ }, import("../workflows/types.js").WorkflowIncludeMap> & {
117
+ nodes: {
118
+ readonly approve: import("../workflows/types.js").WorkflowNodeCommon & {
119
+ nodeType: "checkpoint";
120
+ summary?: string;
121
+ run?: (context: WorkflowNodeContext) => import("../workflows/types.js").MaybePromise<unknown>;
122
+ humanDecision?: import("../workflows/types.js").HumanDecisionNodeContract;
123
+ } & {
124
+ readonly __humanChoices?: {
125
+ readonly continue: {
126
+ readonly label: "Yes, continue";
127
+ };
128
+ readonly stop: {
129
+ readonly label: "No, stop";
130
+ };
131
+ readonly replan: {
132
+ readonly label: "Replan";
133
+ readonly input: import("../workflows/types.js").HumanDecisionTextInput & {
134
+ name: "instructions";
135
+ };
136
+ };
137
+ };
138
+ };
139
+ readonly continued: import("../workflows/types.js").ComputeNodeDefinition;
140
+ readonly stopped: import("../workflows/types.js").ComputeNodeDefinition;
141
+ readonly replan: import("../workflows/types.js").ComputeNodeDefinition;
142
+ };
143
+ includes?: import("../workflows/types.js").WorkflowIncludeMap;
144
+ exits?: {
145
+ readonly continue: {
146
+ readonly from: "continued";
147
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalContinue;
148
+ };
149
+ readonly stop: {
150
+ readonly from: "stopped";
151
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalStop;
152
+ };
153
+ readonly replan: {
154
+ readonly from: "replan";
155
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalReplan;
156
+ };
157
+ };
158
+ }>;
159
+ readonly redesign: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<AutoplanInput, {
160
+ readonly ready: {
161
+ readonly from: "finalize";
162
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanReady;
163
+ };
164
+ readonly blocked: {
165
+ readonly from: "blocked";
166
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanBlocked;
59
167
  };
60
168
  }, import("../workflows/types.js").WorkflowIncludeMap> & {
61
169
  nodes: {
@@ -71,18 +179,22 @@ export declare const autoimplementWorkflow: import("../workflows/types.js").Work
71
179
  exits?: {
72
180
  readonly ready: {
73
181
  readonly from: "finalize";
74
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseReady;
182
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanReady;
75
183
  };
76
184
  readonly blocked: {
77
185
  readonly from: "blocked";
78
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseBlocked;
186
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanBlocked;
79
187
  };
80
188
  };
81
189
  }>;
82
190
  }> & {
83
191
  nodes: {
84
192
  readonly prepare: import("../workflows/types.js").ComputeNodeDefinition;
193
+ readonly findPlan: import("../workflows/types.js").AgentNodeDefinition;
194
+ readonly routeFoundPlan: import("../workflows/types.js").ComputeNodeDefinition;
85
195
  readonly adoptPlan: import("../workflows/types.js").ComputeNodeDefinition;
196
+ readonly maybeApproval: import("../workflows/types.js").ComputeNodeDefinition;
197
+ readonly replanGuard: import("../workflows/types.js").ComputeNodeDefinition;
86
198
  readonly implement: import("../workflows/types.js").AgentNodeDefinition;
87
199
  readonly classifyImplementation: import("../workflows/types.js").AgentNodeDefinition;
88
200
  readonly verify: import("../workflows/types.js").AgentNodeDefinition;
@@ -108,14 +220,101 @@ export declare const autoimplementWorkflow: import("../workflows/types.js").Work
108
220
  readonly finalize: import("../workflows/types.js").ComputeNodeDefinition;
109
221
  };
110
222
  includes?: {
111
- readonly redesign: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<AutodeviseInput, {
223
+ readonly documentation: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<AutodocInput, {
224
+ readonly ready: {
225
+ readonly from: "finalize";
226
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").DocumentedPlan;
227
+ };
228
+ readonly blocked: {
229
+ readonly from: "blocked";
230
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").AutodocBlocked;
231
+ };
232
+ }, import("../workflows/types.js").WorkflowIncludeMap> & {
233
+ nodes: {
234
+ readonly prepare: import("../workflows/types.js").ComputeNodeDefinition;
235
+ readonly locatePlan: import("../workflows/types.js").AgentNodeDefinition;
236
+ readonly inspectDocumentation: import("../workflows/types.js").AgentNodeDefinition;
237
+ readonly updateDocumentation: import("../workflows/types.js").AgentNodeDefinition;
238
+ readonly verifyDocumentation: import("../workflows/types.js").AgentNodeDefinition;
239
+ readonly finalize: import("../workflows/types.js").ComputeNodeDefinition;
240
+ readonly blocked: import("../workflows/types.js").ComputeNodeDefinition;
241
+ };
242
+ includes?: import("../workflows/types.js").WorkflowIncludeMap;
243
+ exits?: {
244
+ readonly ready: {
245
+ readonly from: "finalize";
246
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").DocumentedPlan;
247
+ };
248
+ readonly blocked: {
249
+ readonly from: "blocked";
250
+ readonly validate: (value: unknown) => import("./autodoc.workflow.js").AutodocBlocked;
251
+ };
252
+ };
253
+ }>;
254
+ readonly approval: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<PlanApprovalInput, {
255
+ readonly continue: {
256
+ readonly from: "continued";
257
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalContinue;
258
+ };
259
+ readonly stop: {
260
+ readonly from: "stopped";
261
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalStop;
262
+ };
263
+ readonly replan: {
264
+ readonly from: "replan";
265
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalReplan;
266
+ };
267
+ }, import("../workflows/types.js").WorkflowIncludeMap> & {
268
+ nodes: {
269
+ readonly approve: import("../workflows/types.js").WorkflowNodeCommon & {
270
+ nodeType: "checkpoint";
271
+ summary?: string;
272
+ run?: (context: WorkflowNodeContext) => import("../workflows/types.js").MaybePromise<unknown>;
273
+ humanDecision?: import("../workflows/types.js").HumanDecisionNodeContract;
274
+ } & {
275
+ readonly __humanChoices?: {
276
+ readonly continue: {
277
+ readonly label: "Yes, continue";
278
+ };
279
+ readonly stop: {
280
+ readonly label: "No, stop";
281
+ };
282
+ readonly replan: {
283
+ readonly label: "Replan";
284
+ readonly input: import("../workflows/types.js").HumanDecisionTextInput & {
285
+ name: "instructions";
286
+ };
287
+ };
288
+ };
289
+ };
290
+ readonly continued: import("../workflows/types.js").ComputeNodeDefinition;
291
+ readonly stopped: import("../workflows/types.js").ComputeNodeDefinition;
292
+ readonly replan: import("../workflows/types.js").ComputeNodeDefinition;
293
+ };
294
+ includes?: import("../workflows/types.js").WorkflowIncludeMap;
295
+ exits?: {
296
+ readonly continue: {
297
+ readonly from: "continued";
298
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalContinue;
299
+ };
300
+ readonly stop: {
301
+ readonly from: "stopped";
302
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalStop;
303
+ };
304
+ readonly replan: {
305
+ readonly from: "replan";
306
+ readonly validate: (value: unknown) => import("./plan-approval.workflow.js").PlanApprovalReplan;
307
+ };
308
+ };
309
+ }>;
310
+ readonly redesign: import("../workflows/types.js").WorkflowIncludeDefinition<import("../workflows/types.js").WorkflowDefinition<AutoplanInput, {
112
311
  readonly ready: {
113
312
  readonly from: "finalize";
114
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseReady;
313
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanReady;
115
314
  };
116
315
  readonly blocked: {
117
316
  readonly from: "blocked";
118
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseBlocked;
317
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanBlocked;
119
318
  };
120
319
  }, import("../workflows/types.js").WorkflowIncludeMap> & {
121
320
  nodes: {
@@ -131,11 +330,11 @@ export declare const autoimplementWorkflow: import("../workflows/types.js").Work
131
330
  exits?: {
132
331
  readonly ready: {
133
332
  readonly from: "finalize";
134
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseReady;
333
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanReady;
135
334
  };
136
335
  readonly blocked: {
137
336
  readonly from: "blocked";
138
- readonly validate: (value: unknown) => import("./autodevise.workflow.js").AutodeviseBlocked;
337
+ readonly validate: (value: unknown) => import("./autoplan.workflow.js").AutoplanBlocked;
139
338
  };
140
339
  };
141
340
  }>;
@@ -1,6 +1,9 @@
1
1
  import path from "node:path";
2
2
  import { agent, compute, defineWorkflow, includeWorkflow, includedResult, shell, } from "../workflows/definition.js";
3
- import autodeviseWorkflow, {} from "./autodevise.workflow.js";
3
+ import { digest } from "../workflows/human-decision.js";
4
+ import autodocWorkflow, {} from "./autodoc.workflow.js";
5
+ import autoplanWorkflow, {} from "./autoplan.workflow.js";
6
+ import planApprovalWorkflow, {} from "./plan-approval.workflow.js";
4
7
  const FIVE_MINUTES_MS = 5 * 60_000;
5
8
  const TEN_MINUTES_MS = 10 * 60_000;
6
9
  function requireRecord(value, label) {
@@ -25,6 +28,47 @@ function parseInput(value) {
25
28
  if (input.merge !== undefined && typeof input.merge !== "boolean") {
26
29
  throw new Error("autoimplement merge must be a boolean");
27
30
  }
31
+ const documents = input.documents;
32
+ if (documents !== undefined &&
33
+ (!Array.isArray(documents) || documents.some((item) => typeof item !== "string"))) {
34
+ throw new Error("autoimplement documents must be an array of strings");
35
+ }
36
+ let documentation;
37
+ if (input.documentation !== undefined) {
38
+ if (input.plan === undefined) {
39
+ throw new Error("autoimplement documentation requires an explicit plan");
40
+ }
41
+ const raw = requireRecord(input.documentation, "autoimplement documentation");
42
+ if (raw.status !== "current") {
43
+ throw new Error("autoimplement documentation status must be current");
44
+ }
45
+ const planDigest = requireString(raw.planDigest, "autoimplement documentation planDigest");
46
+ if (planDigest !== digest(input.plan)) {
47
+ throw new Error("autoimplement documentation planDigest does not match the explicit plan");
48
+ }
49
+ if (!Array.isArray(raw.documents) || raw.documents.some((item) => typeof item !== "string")) {
50
+ throw new Error("autoimplement documentation documents must be an array of strings");
51
+ }
52
+ documentation = {
53
+ status: "current",
54
+ planDigest,
55
+ documents: [...raw.documents],
56
+ };
57
+ }
58
+ let approval;
59
+ if (input.approval !== undefined) {
60
+ const raw = requireRecord(input.approval, "autoimplement approval");
61
+ const maxReplans = raw.maxReplans ?? 3;
62
+ if (!Number.isInteger(maxReplans) ||
63
+ maxReplans < 1 ||
64
+ maxReplans > 20) {
65
+ throw new Error("autoimplement approval maxReplans must be from 1 through 20");
66
+ }
67
+ approval = {
68
+ audience: requireString(raw.audience, "autoimplement approval audience"),
69
+ maxReplans: maxReplans,
70
+ };
71
+ }
28
72
  return {
29
73
  task: requireString(input.task, "autoimplement task"),
30
74
  ...(input.plan !== undefined ? { plan: input.plan } : {}),
@@ -37,6 +81,38 @@ function parseInput(value) {
37
81
  ? { baseBranch: requireString(input.baseBranch, "baseBranch") }
38
82
  : {}),
39
83
  merge: input.merge === true,
84
+ ...(documents !== undefined ? { documents: [...documents] } : {}),
85
+ ...(documentation !== undefined ? { documentation } : {}),
86
+ ...(approval !== undefined ? { approval } : {}),
87
+ };
88
+ }
89
+ function parseExistingPlan(value) {
90
+ const result = requireRecord(value, "existing plan discovery");
91
+ if (result.route !== "found" && result.route !== "blocked") {
92
+ throw new Error("existing plan discovery route must be found or blocked");
93
+ }
94
+ if (result.route === "found") {
95
+ if (result.plan === undefined)
96
+ throw new Error("found plan must include plan");
97
+ if (result.documentation !== "current" &&
98
+ result.documentation !== "missing" &&
99
+ result.documentation !== "stale") {
100
+ throw new Error("found plan documentation must be current, missing, or stale");
101
+ }
102
+ }
103
+ if (!Array.isArray(result.documents) ||
104
+ result.documents.some((item) => typeof item !== "string")) {
105
+ throw new Error("existing plan documents must be an array of strings");
106
+ }
107
+ return {
108
+ route: result.route,
109
+ ...(result.plan !== undefined ? { plan: result.plan } : {}),
110
+ ...(result.documentation !== undefined
111
+ ? { documentation: result.documentation }
112
+ : {}),
113
+ documents: [...result.documents],
114
+ reason: requireString(result.reason, "existing plan discovery reason"),
115
+ evidence: result.evidence ?? null,
40
116
  };
41
117
  }
42
118
  function parseRoute(value, routes, label) {
@@ -124,9 +200,39 @@ function latestCiCommand(context) {
124
200
  }
125
201
  function currentPlan(context) {
126
202
  const adopted = context.outputs.adoptPlan;
127
- return adopted?.plan ?? context.input.plan;
203
+ if (adopted?.plan !== undefined)
204
+ return adopted.plan;
205
+ const documented = context.outputs.documentation;
206
+ if (documented?.exit === "ready" && documented.output?.plan !== undefined) {
207
+ return documented.output.plan;
208
+ }
209
+ const discovered = context.outputs.findPlan;
210
+ if (discovered?.route === "found" && discovered.plan !== undefined)
211
+ return discovered.plan;
212
+ return context.input.plan;
213
+ }
214
+ function currentPlanDigest(context) {
215
+ const adopted = context.outputs.adoptPlan;
216
+ if (typeof adopted?.planDigest === "string")
217
+ return adopted.planDigest;
218
+ const documented = context.outputs.documentation;
219
+ if (documented?.exit === "ready" && typeof documented.output?.planDigest === "string") {
220
+ return documented.output.planDigest;
221
+ }
222
+ const plan = currentPlan(context);
223
+ if (plan === undefined)
224
+ throw new Error("autoimplement does not have a selected plan");
225
+ return digest(plan);
128
226
  }
129
227
  function latestIssue(context) {
228
+ const approval = context.outputs.approval;
229
+ if (approval?.exit === "replan" && typeof approval.output?.instructions === "string") {
230
+ return {
231
+ source: "human-replan",
232
+ instructions: approval.output.instructions,
233
+ priorPlanDigest: currentPlanDigest(context),
234
+ };
235
+ }
130
236
  const ids = [
131
237
  "classifyImplementation",
132
238
  "classifyVerification",
@@ -198,7 +304,11 @@ function latestBlockedReason(context) {
198
304
  "classifyImplementation",
199
305
  "classifyVerification",
200
306
  "triageReview",
307
+ "replanGuard",
201
308
  "adoptPlan",
309
+ "findPlan",
310
+ "documentation",
311
+ "approval",
202
312
  ];
203
313
  for (let index = context.state.steps.length - 1; index >= 0; index -= 1) {
204
314
  const step = context.state.steps[index];
@@ -222,11 +332,46 @@ export const autoimplementWorkflow = defineWorkflow({
222
332
  title: ({ input }) => `autoimplement: ${input.task.slice(0, 60)}`,
223
333
  presentationPrompt: "Summarize what was implemented, the review rounds by severity, the CI result, the PR or merge result, and any remaining limitation. Include exact validation commands.",
224
334
  startAt: "prepare",
225
- maxSteps: 160,
335
+ maxSteps: 240,
226
336
  includes: {
337
+ documentation: includeWorkflow(autodocWorkflow, {
338
+ input: (context) => {
339
+ const request = context.input;
340
+ const discovery = context.outputs.findPlan;
341
+ const plan = currentPlan(context);
342
+ if (plan === undefined)
343
+ throw new Error("autoimplement documentation is missing a plan");
344
+ return {
345
+ task: request.task,
346
+ plan,
347
+ ...(request.repository !== undefined ? { repository: request.repository } : {}),
348
+ documents: request.documents ?? request.documentation?.documents ?? discovery?.documents ?? [],
349
+ evidence: latestIssue(context),
350
+ };
351
+ },
352
+ }),
353
+ approval: includeWorkflow(planApprovalWorkflow, {
354
+ input: (context) => {
355
+ const request = context.input;
356
+ if (request.approval === undefined) {
357
+ throw new Error("autoimplement approval was entered without an approval policy");
358
+ }
359
+ const plan = currentPlan(context);
360
+ if (plan === undefined)
361
+ throw new Error("autoimplement approval is missing a plan");
362
+ const revisions = context.state.steps.filter((step) => step.nodeId === "approval/approve").length;
363
+ return {
364
+ task: request.task,
365
+ plan,
366
+ planDigest: currentPlanDigest(context),
367
+ audience: request.approval.audience,
368
+ revision: revisions + 1,
369
+ };
370
+ },
371
+ }),
227
372
  redesign: includeWorkflow({
228
- workflow: "autodevise",
229
- contract: autodeviseWorkflow,
373
+ workflow: "autoplan",
374
+ contract: autoplanWorkflow,
230
375
  input: (context) => {
231
376
  const request = context.input;
232
377
  return {
@@ -251,26 +396,84 @@ export const autoimplementWorkflow = defineWorkflow({
251
396
  },
252
397
  nodes: {
253
398
  prepare: compute({
254
- run: ({ input }) => ({
255
- route: input.plan === undefined ? "redesign" : "implement",
256
- }),
399
+ run: ({ input }) => {
400
+ const request = input;
401
+ return {
402
+ route: request.plan === undefined
403
+ ? "find"
404
+ : request.documentation?.status === "current"
405
+ ? "ready"
406
+ : "document",
407
+ };
408
+ },
409
+ }),
410
+ findPlan: agent({
411
+ statusDetail: "finding existing plan",
412
+ prompt: ({ input }) => {
413
+ const request = input;
414
+ return [
415
+ "Find the clear plan that has already been selected for this task.",
416
+ "Use the current conversation context and referenced canonical documents.",
417
+ "Do not devise, improve, replace, document, or implement a plan.",
418
+ "Return blocked when no single clear existing plan can be found.",
419
+ "Report whether its canonical documentation is current, missing, or stale.",
420
+ `Task: ${request.task}`,
421
+ `Repository: ${request.repository ?? "current repository"}`,
422
+ `Referenced documents: ${JSON.stringify(request.documents ?? [])}`,
423
+ ].join("\n");
424
+ },
425
+ expectedOutput: '{ "route": "found" | "blocked", "plan": {} (required when found), "documentation": "current" | "missing" | "stale" (required when found), "documents": ["canonical file"], "reason": "reason", "evidence": "evidence" }',
426
+ validate: parseExistingPlan,
427
+ }),
428
+ routeFoundPlan: compute({
429
+ run: ({ outputs }) => {
430
+ const discovered = outputs.findPlan;
431
+ if (discovered.route !== "found" || discovered.plan === undefined) {
432
+ return { route: "blocked", reason: discovered.reason, evidence: discovered.evidence };
433
+ }
434
+ return {
435
+ route: discovered.documentation === "current" ? "ready" : "document",
436
+ plan: discovered.plan,
437
+ reason: discovered.reason,
438
+ evidence: discovered.evidence,
439
+ };
440
+ },
257
441
  }),
258
442
  adoptPlan: compute({
259
443
  run: ({ outputs }) => {
260
- const result = includedResult(autodeviseWorkflow, outputs.redesign);
444
+ const result = includedResult(autoplanWorkflow, outputs.redesign);
261
445
  if (result.exit !== "ready")
262
446
  throw new Error("redesign did not return a ready plan");
263
447
  return {
264
- route: result.output.changed ? "implement" : "blocked",
448
+ route: result.output.changed ? "document" : "blocked",
265
449
  plan: result.output.plan,
266
450
  planDigest: result.output.planDigest,
267
451
  changed: result.output.changed,
268
452
  reason: result.output.changed
269
- ? "The plan changed in response to new evidence."
453
+ ? "The plan changed in response to new evidence and must be documented."
270
454
  : "Redesign returned the same plan for the same unresolved evidence.",
271
455
  };
272
456
  },
273
457
  }),
458
+ maybeApproval: compute({
459
+ run: ({ input }) => ({
460
+ route: input.approval === undefined ? "implement" : "approve",
461
+ }),
462
+ }),
463
+ replanGuard: compute({
464
+ run: (context) => {
465
+ const request = context.input;
466
+ const limit = request.approval?.maxReplans ?? 3;
467
+ const replans = context.state.steps.filter((step) => step.nodeId === "approval/replan").length;
468
+ return replans > limit
469
+ ? {
470
+ route: "blocked",
471
+ reason: `Plan approval reached the ${limit}-replan safety limit.`,
472
+ evidence: context.outputs.approval,
473
+ }
474
+ : { route: "redesign", replans, limit };
475
+ },
476
+ }),
274
477
  implement: agent({
275
478
  timeoutMs: 60 * 60_000,
276
479
  statusDetail: "implementing",
@@ -279,6 +482,8 @@ export const autoimplementWorkflow = defineWorkflow({
279
482
  return [
280
483
  `Implement this task end-to-end: ${request.task}`,
281
484
  `Plan: ${JSON.stringify(currentPlan(context))}`,
485
+ `Authorized scope: ${request.scope ?? request.repository ?? "the current repository and task"}`,
486
+ `Constraints: ${JSON.stringify(request.constraints ?? [])}`,
282
487
  "Follow repository instructions and use the most elegant long-term production-ready implementation without unnecessary work.",
283
488
  "If implementation exposes a new design or scope problem, report it precisely instead of forcing the old plan.",
284
489
  "Do not merge yet.",
@@ -601,13 +806,40 @@ export const autoimplementWorkflow = defineWorkflow({
601
806
  edges: [
602
807
  {
603
808
  from: "prepare",
604
- switch: { on: "$.route", cases: { redesign: "redesign", implement: "implement" } },
809
+ switch: {
810
+ on: "$.route",
811
+ cases: { find: "findPlan", document: "documentation", ready: "maybeApproval" },
812
+ },
813
+ },
814
+ {
815
+ from: "findPlan",
816
+ switch: { on: "$.route", cases: { found: "routeFoundPlan", blocked: "blocked" } },
817
+ },
818
+ {
819
+ from: "routeFoundPlan",
820
+ switch: {
821
+ on: "$.route",
822
+ cases: { ready: "maybeApproval", document: "documentation", blocked: "blocked" },
823
+ },
605
824
  },
606
825
  { from: "redesign.ready", to: "adoptPlan" },
607
826
  { from: "redesign.blocked", to: "blocked" },
608
827
  {
609
828
  from: "adoptPlan",
610
- switch: { on: "$.route", cases: { implement: "implement", blocked: "blocked" } },
829
+ switch: { on: "$.route", cases: { document: "documentation", blocked: "blocked" } },
830
+ },
831
+ { from: "documentation.ready", to: "maybeApproval" },
832
+ { from: "documentation.blocked", to: "blocked" },
833
+ {
834
+ from: "maybeApproval",
835
+ switch: { on: "$.route", cases: { approve: "approval", implement: "implement" } },
836
+ },
837
+ { from: "approval.continue", to: "implement" },
838
+ { from: "approval.stop", to: "blocked" },
839
+ { from: "approval.replan", to: "replanGuard" },
840
+ {
841
+ from: "replanGuard",
842
+ switch: { on: "$.route", cases: { redesign: "redesign", blocked: "blocked" } },
611
843
  },
612
844
  { from: "implement", to: "classifyImplementation" },
613
845
  {