@osolmaz/pi-workflows 0.13.2 → 0.13.4

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 (134) hide show
  1. package/README.md +5 -3
  2. package/dist/builtins/autodoc.workflow.d.ts +5 -5
  3. package/dist/builtins/autoimplement.workflow.d.ts +77 -73
  4. package/dist/builtins/autoimplement.workflow.js +47 -8
  5. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  6. package/dist/builtins/autoplan.workflow.d.ts +8 -8
  7. package/dist/builtins/autoplan.workflow.js +44 -35
  8. package/dist/builtins/autoplan.workflow.js.map +1 -1
  9. package/dist/builtins/catalog.js +2 -2
  10. package/dist/builtins/change-verification.workflow.d.ts +1 -1
  11. package/dist/builtins/plain-summary.workflow.d.ts +1 -1
  12. package/dist/builtins/plan-approval.workflow.d.ts +1 -1
  13. package/dist/builtins/plan-change.workflow.d.ts +29 -29
  14. package/dist/builtins/sanity-check.workflow.d.ts +3 -3
  15. package/dist/builtins/workspace-preparation.workflow.d.ts +1 -1
  16. package/dist/controllers/index.d.ts +2 -2
  17. package/dist/controllers/index.js.map +1 -1
  18. package/dist/controllers/sqlite.js +85 -23
  19. package/dist/controllers/sqlite.js.map +1 -1
  20. package/dist/controllers/types.d.ts +34 -0
  21. package/dist/controllers/workflow-engine-scheduler.d.ts +5 -1
  22. package/dist/controllers/workflow-engine-scheduler.js +83 -1
  23. package/dist/controllers/workflow-engine-scheduler.js.map +1 -1
  24. package/dist/controllers/workflows.d.ts +8 -1
  25. package/dist/controllers/workflows.js +36 -0
  26. package/dist/controllers/workflows.js.map +1 -1
  27. package/dist/extension/controller-host.d.ts +2 -1
  28. package/dist/extension/controller-host.js +1 -1
  29. package/dist/extension/controller-host.js.map +1 -1
  30. package/dist/extension/follow-up-coordinator.d.ts +27 -0
  31. package/dist/extension/follow-up-coordinator.js +131 -0
  32. package/dist/extension/follow-up-coordinator.js.map +1 -0
  33. package/dist/extension/index.d.ts +9 -0
  34. package/dist/extension/index.js +460 -75
  35. package/dist/extension/index.js.map +1 -1
  36. package/dist/extension/session-events.d.ts +2 -3
  37. package/dist/extension/session-events.js +11 -10
  38. package/dist/extension/session-events.js.map +1 -1
  39. package/dist/extension/widget.js +9 -0
  40. package/dist/extension/widget.js.map +1 -1
  41. package/dist/state/database.d.ts +2 -1
  42. package/dist/state/database.js +11 -13
  43. package/dist/state/database.js.map +1 -1
  44. package/dist/state/json.js +6 -1
  45. package/dist/state/json.js.map +1 -1
  46. package/dist/state/mutation.d.ts +1 -1
  47. package/dist/state/mutation.js.map +1 -1
  48. package/dist/state/prune.d.ts +18 -0
  49. package/dist/state/prune.js +373 -0
  50. package/dist/state/prune.js.map +1 -0
  51. package/dist/state/schema.d.ts +1 -1
  52. package/dist/state/schema.js +116 -18
  53. package/dist/state/schema.js.map +1 -1
  54. package/dist/viewer/cli.d.ts +3 -1
  55. package/dist/viewer/cli.js +55 -4
  56. package/dist/viewer/cli.js.map +1 -1
  57. package/dist/viewer/render.js +19 -1
  58. package/dist/viewer/render.js.map +1 -1
  59. package/dist/workflows/composition.d.ts +2 -0
  60. package/dist/workflows/composition.js +15 -0
  61. package/dist/workflows/composition.js.map +1 -1
  62. package/dist/workflows/definition.d.ts +6 -3
  63. package/dist/workflows/definition.js +3 -0
  64. package/dist/workflows/definition.js.map +1 -1
  65. package/dist/workflows/engine.d.ts +6 -1
  66. package/dist/workflows/engine.js +301 -35
  67. package/dist/workflows/engine.js.map +1 -1
  68. package/dist/workflows/human-decision.d.ts +1 -0
  69. package/dist/workflows/human-decision.js +25 -9
  70. package/dist/workflows/human-decision.js.map +1 -1
  71. package/dist/workflows/index.d.ts +3 -1
  72. package/dist/workflows/index.js +2 -0
  73. package/dist/workflows/index.js.map +1 -1
  74. package/dist/workflows/json-patch.d.ts +47 -0
  75. package/dist/workflows/json-patch.js +298 -0
  76. package/dist/workflows/json-patch.js.map +1 -0
  77. package/dist/workflows/schema.js +29 -1
  78. package/dist/workflows/schema.js.map +1 -1
  79. package/dist/workflows/settings.d.ts +128 -0
  80. package/dist/workflows/settings.js +199 -0
  81. package/dist/workflows/settings.js.map +1 -0
  82. package/dist/workflows/store.d.ts +75 -2
  83. package/dist/workflows/store.js +1349 -89
  84. package/dist/workflows/store.js.map +1 -1
  85. package/dist/workflows/tool-input.d.ts +22 -0
  86. package/dist/workflows/tool-input.js +43 -0
  87. package/dist/workflows/tool-input.js.map +1 -1
  88. package/dist/workflows/types.d.ts +40 -5
  89. package/docs/2026-08-25-workflow-follow-ups.md +132 -0
  90. package/docs/2026-08-25-workflow-settings.md +169 -0
  91. package/docs/DEFERRED_TURNS.md +6 -0
  92. package/docs/DESIGN_PHILOSOPHY.md +2 -0
  93. package/docs/SQLITE_STATE.md +31 -18
  94. package/docs/WORKFLOW_COMPOSITION.md +16 -1
  95. package/docs/WORKFLOW_STEP_MESSAGES.md +4 -2
  96. package/docs/plans/2026-08-25-live-workflow-settings-plan.md +532 -0
  97. package/docs/plans/2026-08-25-workflow-run-storage-plan.md +67 -0
  98. package/docs/tui-viewer.md +7 -6
  99. package/docs/workflows.md +22 -1
  100. package/examples/workflows/live-settings.workflow.ts +93 -0
  101. package/herdr-plugin.toml +1 -1
  102. package/package.json +1 -1
  103. package/skills/autoplan/SKILL.md +6 -6
  104. package/src/builtins/autoimplement.workflow.ts +56 -8
  105. package/src/builtins/autoplan.workflow.ts +45 -37
  106. package/src/builtins/catalog.ts +2 -2
  107. package/src/controllers/index.ts +6 -0
  108. package/src/controllers/sqlite.ts +129 -35
  109. package/src/controllers/types.ts +40 -0
  110. package/src/controllers/workflow-engine-scheduler.ts +103 -1
  111. package/src/controllers/workflows.ts +68 -0
  112. package/src/extension/controller-host.ts +6 -1
  113. package/src/extension/follow-up-coordinator.ts +151 -0
  114. package/src/extension/index.ts +538 -88
  115. package/src/extension/session-events.ts +15 -13
  116. package/src/extension/widget.ts +8 -0
  117. package/src/state/database.ts +12 -12
  118. package/src/state/json.ts +6 -1
  119. package/src/state/mutation.ts +4 -1
  120. package/src/state/prune.ts +502 -0
  121. package/src/state/schema.ts +116 -18
  122. package/src/viewer/cli.ts +52 -5
  123. package/src/viewer/render.ts +43 -1
  124. package/src/workflows/composition.ts +19 -0
  125. package/src/workflows/definition.ts +19 -5
  126. package/src/workflows/engine.ts +363 -35
  127. package/src/workflows/human-decision.ts +41 -11
  128. package/src/workflows/index.ts +43 -0
  129. package/src/workflows/json-patch.ts +375 -0
  130. package/src/workflows/schema.ts +31 -1
  131. package/src/workflows/settings.ts +430 -0
  132. package/src/workflows/store.ts +1977 -184
  133. package/src/workflows/tool-input.ts +58 -0
  134. package/src/workflows/types.ts +43 -4
@@ -0,0 +1,93 @@
1
+ import {
2
+ agent,
3
+ allowSettingsPath,
4
+ compute,
5
+ defineWorkflow,
6
+ settingsRoute,
7
+ workflowSettings,
8
+ } from "@osolmaz/pi-workflows";
9
+
10
+ type Settings = {
11
+ instructions: string[];
12
+ route: "normal" | "careful";
13
+ variables: Record<string, unknown>;
14
+ };
15
+
16
+ function parseSettings(value: unknown): Settings {
17
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
18
+ throw new Error("settings must be an object");
19
+ }
20
+ const settings = value as Partial<Settings>;
21
+ if (
22
+ !Array.isArray(settings.instructions) ||
23
+ !settings.instructions.every((item) => typeof item === "string") ||
24
+ (settings.route !== "normal" && settings.route !== "careful") ||
25
+ settings.variables === null ||
26
+ typeof settings.variables !== "object" ||
27
+ Array.isArray(settings.variables)
28
+ ) {
29
+ throw new Error("settings are invalid");
30
+ }
31
+ return {
32
+ instructions: [...settings.instructions],
33
+ route: settings.route,
34
+ variables: { ...settings.variables },
35
+ };
36
+ }
37
+
38
+ export default defineWorkflow({
39
+ name: "live-settings",
40
+ settings: workflowSettings<Settings>({
41
+ initial: { instructions: [], route: "normal", variables: {} },
42
+ parse: parseSettings,
43
+ description: "Instructions, variables, and the future route for this example.",
44
+ paths: [
45
+ allowSettingsPath("/instructions", {
46
+ read: ["session", "human"],
47
+ add: ["session", "human"],
48
+ remove: ["session", "human"],
49
+ replace: ["session", "human"],
50
+ }),
51
+ allowSettingsPath("/route", {
52
+ read: ["session", "human"],
53
+ replace: ["session", "human"],
54
+ }),
55
+ allowSettingsPath("/variables", {
56
+ read: ["session", "human"],
57
+ add: ["session", "human"],
58
+ remove: ["session", "human"],
59
+ replace: ["session", "human"],
60
+ }),
61
+ ],
62
+ }),
63
+ startAt: "prepare",
64
+ nodes: {
65
+ prepare: agent({
66
+ prompt: ({ settings }) =>
67
+ [
68
+ "Prepare the requested work.",
69
+ `Current instructions and variables: ${JSON.stringify(settings)}`,
70
+ "The user can change future settings or queue several post-completion prompts while this step runs.",
71
+ ].join("\n"),
72
+ expectedOutput: `{ "prepared": "summary" }`,
73
+ }),
74
+ choose: settingsRoute({
75
+ run: ({ settings }) => ({ route: (settings as Settings).route }),
76
+ }),
77
+ normal: compute({
78
+ run: ({ settings }) => ({ mode: "normal", settings }),
79
+ }),
80
+ careful: compute({
81
+ run: ({ settings }) => ({ mode: "careful", settings }),
82
+ }),
83
+ },
84
+ edges: [
85
+ { from: "prepare", to: "choose" },
86
+ {
87
+ from: "choose",
88
+ switch: { on: "$.route", cases: { normal: "normal", careful: "careful" } },
89
+ },
90
+ ],
91
+ presentationPrompt:
92
+ "Explain which route and settings were used. Any queued follow-up prompts run only after this response settles.",
93
+ });
package/herdr-plugin.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  id = "osolmaz.pi-workflows"
2
2
  name = "pi-workflows"
3
- version = "0.13.2"
3
+ version = "0.13.4"
4
4
  min_herdr_version = "0.7.0"
5
5
  description = "Open the active pi-workflows run in piw from a managed Herdr pane."
6
6
  platforms = ["linux", "macos"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osolmaz/pi-workflows",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
4
4
  "description": "Workflow and controller runtime with a live terminal viewer for the pi coding agent",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: autoplan
3
- description: Use when the user asks to devise, choose, or plan the most elegant long-term production-ready solution, compare it with the ideal end state, and produce the best practical in-scope implementation plan without asking the user to resolve the gap.
3
+ description: Use when the user asks to devise, choose, or plan the most Long term elegant and production ready solution, compare it with the Holy grail, and produce the best practical in-scope implementation plan without asking the user to resolve the gap.
4
4
  compatibility: Requires pi-workflows and the built-in autoplan workflow.
5
5
  ---
6
6
 
@@ -37,12 +37,12 @@ When this skill is loaded inside an active workflow step, do not start another w
37
37
  Outside Pi, or when the workflow is unavailable:
38
38
 
39
39
  1. Frame the problem, observable success criteria, scope, constraints, and interfaces under our control.
40
- 2. Record two through four distinct practical candidates. Give each one a stable id, short title, plain gist, full solution, rationale, parts, and trade-offs.
41
- 3. Describe the holy grail separately. Name every dependency outside our authority.
40
+ 2. Record two through four distinct practical candidates. Ask whether each is a Long term elegant and production ready solution. Give each one a stable id, short title, plain gist, full solution, rationale, parts, and trade-offs.
41
+ 3. Describe the Holy grail separately. Ask whether it is the Holy grail for the problem. Name every dependency outside our authority.
42
42
  4. Choose the right option without asking the user to decide between them. Record one rejection reason for every other explicit candidate.
43
- - Choose the ideal when it is proportionate, production-ready, in scope, and implementable through interfaces we control.
44
- - Otherwise choose the strongest practical in-scope solution with a clear path toward the ideal.
45
- - Do not block only because the ideal requires an upstream or external change.
43
+ - Choose the Holy grail when it is proportionate, production-ready, in scope, and implementable through interfaces we control.
44
+ - Otherwise choose the strongest practical in-scope solution with a clear path toward the Holy grail.
45
+ - Do not block only because the Holy grail requires an upstream or external change.
46
46
  5. Write a detailed implementation plan. For each step, state what changes, where it changes, and how to verify it.
47
47
  6. Present one short plain assistant message with the chosen plan, its main steps, and a one-line gist and rejection reason for every other candidate. Call it selected for approval when a later human decision still applies.
48
48
  7. Stop as blocked only when no truthful in-scope solution can meet the success criteria.
@@ -13,6 +13,7 @@ import {
13
13
  includedResult,
14
14
  } from "../workflows/definition.js";
15
15
  import { digest } from "../workflows/human-decision.js";
16
+ import { allowSettingsPath, workflowSettings } from "../workflows/settings.js";
16
17
  import type { WorkflowActionContext, WorkflowNodeContext } from "../workflows/types.js";
17
18
  import autodocWorkflow, { type AutodocInput } from "./autodoc.workflow.js";
18
19
  import {
@@ -40,6 +41,11 @@ import workspacePreparationWorkflow, {
40
41
  type WorkspacePreparationInput,
41
42
  } from "./workspace-preparation.workflow.js";
42
43
 
44
+ export type AutoimplementSettings = {
45
+ merge: boolean;
46
+ addedInstructions: string[];
47
+ };
48
+
43
49
  export type AutoimplementInput = {
44
50
  task: string;
45
51
  plan?: unknown;
@@ -577,6 +583,27 @@ function parseInput(value: unknown): AutoimplementInput {
577
583
  };
578
584
  }
579
585
 
586
+ function parseAutoimplementSettings(value: unknown): AutoimplementSettings {
587
+ const settings = requireRecord(value, "autoimplement settings");
588
+ if (typeof settings.merge !== "boolean") {
589
+ throw new Error("autoimplement settings merge must be a boolean");
590
+ }
591
+ if (
592
+ !Array.isArray(settings.addedInstructions) ||
593
+ settings.addedInstructions.some((item) => typeof item !== "string")
594
+ ) {
595
+ throw new Error("autoimplement settings addedInstructions must be an array of strings");
596
+ }
597
+ return {
598
+ merge: settings.merge,
599
+ addedInstructions: [...settings.addedInstructions] as string[],
600
+ };
601
+ }
602
+
603
+ function autoimplementSettings(context: WorkflowNodeContext): AutoimplementSettings {
604
+ return parseAutoimplementSettings(context.settings);
605
+ }
606
+
580
607
  function parseExistingPlan(value: unknown): ExistingPlanDiscovery {
581
608
  const result = requireRecord(value, "existing plan discovery");
582
609
  if (result.route !== "found" && result.route !== "blocked") {
@@ -790,7 +817,7 @@ function createBlockerClaim(context: WorkflowNodeContext): BlockerClaim {
790
817
  unrelatedFailures: Array.isArray(result.unrelatedFailures) ? result.unrelatedFailures : [],
791
818
  recoveryAttempts: Array.isArray(result.repairAttempts) ? result.repairAttempts : [],
792
819
  alternativesChecked: [],
793
- authorityFact: `scope=${(context.input as AutoimplementInput).scope ?? "unspecified"}; merge=${(context.input as AutoimplementInput).merge === true}`,
820
+ authorityFact: `scope=${(context.input as AutoimplementInput).scope ?? "unspecified"}; merge=${autoimplementSettings(context).merge}`,
794
821
  };
795
822
  }
796
823
 
@@ -919,8 +946,8 @@ function parseDeliveryResult(
919
946
  if (result.status !== "completed" && result.status !== "blocked") {
920
947
  throw new Error("delivery status must be completed or blocked");
921
948
  }
922
- const request = context.input as AutoimplementInput;
923
- if (request.merge !== true && result.merged === true) {
949
+ const settings = autoimplementSettings(context);
950
+ if (settings.merge !== true && result.merged === true) {
924
951
  throw new Error("delivery cannot merge without explicit merge: true");
925
952
  }
926
953
  if (result.status === "blocked") return result;
@@ -960,7 +987,7 @@ function parseDeliveryResult(
960
987
  }
961
988
  actual.set(repository.repository, repository);
962
989
  }
963
- const mergeExpected = request.merge === true;
990
+ const mergeExpected = settings.merge === true;
964
991
  for (const expected of published) {
965
992
  const repository = actual.get(path.resolve(expected.repository));
966
993
  if (repository === undefined || repository.pr !== expected.pr) {
@@ -1298,6 +1325,28 @@ export const autoimplementWorkflow = defineWorkflow({
1298
1325
  contractId: "pi-workflows.autoimplement.v1",
1299
1326
  name: "autoimplement",
1300
1327
  input: parseInput,
1328
+ settings: workflowSettings<AutoimplementSettings, AutoimplementInput>({
1329
+ initial: (input) => ({ merge: input.merge === true, addedInstructions: [] }),
1330
+ parse: parseAutoimplementSettings,
1331
+ description: "Future merge behavior and instructions added during this run.",
1332
+ paths: [
1333
+ allowSettingsPath("/merge", {
1334
+ read: ["session", "human"],
1335
+ replace: ["session", "human"],
1336
+ }),
1337
+ allowSettingsPath("/addedInstructions", {
1338
+ read: ["session", "human"],
1339
+ add: ["session", "human"],
1340
+ remove: ["session", "human"],
1341
+ replace: ["session", "human"],
1342
+ }),
1343
+ ],
1344
+ validateChange: ({ before, after, actor }) => {
1345
+ if (actor.type === "session" && before.merge === false && after.merge === true) {
1346
+ throw new Error("A model workflow step cannot grant merge authority");
1347
+ }
1348
+ },
1349
+ }),
1301
1350
  title: ({ input }) => `autoimplement: ${input.task.slice(0, 60)}`,
1302
1351
  presentationPrompt:
1303
1352
  "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.",
@@ -1616,7 +1665,7 @@ export const autoimplementWorkflow = defineWorkflow({
1616
1665
  `Current result and claimed blocker: ${JSON.stringify(latestBlockerClaim(context))}`,
1617
1666
  `Authorized scope: ${request.scope ?? request.repository ?? "the current repository and task"}`,
1618
1667
  `Constraints and authority: ${JSON.stringify(request.constraints ?? [])}`,
1619
- `Merge authorized: ${request.merge === true}`,
1668
+ `Merge currently allowed: ${autoimplementSettings(context).merge === true}`,
1620
1669
  `Previous blocker challenges: ${JSON.stringify(blockerChallenges(context))}`,
1621
1670
  `Recent workflow attempts: ${JSON.stringify(recentWorkflowAttempts(context))}`,
1622
1671
  ].join("\n");
@@ -1925,10 +1974,9 @@ export const autoimplementWorkflow = defineWorkflow({
1925
1974
  timeoutMs: 30 * 60_000,
1926
1975
  statusDetail: "finalizing PRs",
1927
1976
  prompt: (context) => {
1928
- const request = context.input as AutoimplementInput;
1929
1977
  return [
1930
- request.merge === false
1931
- ? "Leave every verified PR ready without merging because input disabled merge."
1978
+ autoimplementSettings(context).merge === false
1979
+ ? "Leave every verified PR ready without merging because current workflow settings disable merge."
1932
1980
  : "Handle verified PRs one at a time and merge each unless repository policy or explicit user instructions prohibit it.",
1933
1981
  "Before each mutation, inspect the current PR head, merge state, and existing final report. Do not merge an already merged expected head or post a duplicate report.",
1934
1982
  "Use each repository's required merge method.",
@@ -96,9 +96,11 @@ function originalUserInstructions(outputs: Record<string, unknown>): string {
96
96
  }
97
97
 
98
98
  function originalInstructionsPrompt(outputs: Record<string, unknown>): string {
99
- return ["Original user instructions (authoritative):", originalUserInstructions(outputs)].join(
100
- "\n",
101
- );
99
+ return [
100
+ "Continue in this Pi session. Do not delegate this workflow step or start another session.",
101
+ "Original user instructions (authoritative):",
102
+ originalUserInstructions(outputs),
103
+ ].join("\n");
102
104
  }
103
105
 
104
106
  function requireString(value: unknown, label: string): string {
@@ -286,7 +288,7 @@ function candidateSummary(
286
288
  ideal: AutoplanIdeal,
287
289
  id: string,
288
290
  ): { id: string; title: string; gist: string } {
289
- if (id === "ideal") return { id, title: "Ideal end state", gist: ideal.ideal };
291
+ if (id === "ideal") return { id, title: "Holy grail", gist: ideal.ideal };
290
292
  const candidate = proposal.candidates.find((item) => item.id === id);
291
293
  if (candidate === undefined)
292
294
  throw new Error(`autoplan candidate ${JSON.stringify(id)} is missing`);
@@ -298,9 +300,9 @@ function summaryInput(
298
300
  blocked: boolean,
299
301
  input: AutoplanInput,
300
302
  ): PlainSummaryInput {
301
- const proposal = outputs.propose as AutoplanProposal;
302
- const selection = outputs.choose as AutoplanSelection;
303
- const ideal = outputs.ideal as AutoplanIdeal;
303
+ const proposal = outputs.solutions as AutoplanProposal;
304
+ const selection = outputs.select as AutoplanSelection;
305
+ const ideal = outputs.holyGrail as AutoplanIdeal;
304
306
  const selected = candidateSummary(proposal, ideal, selection.selectedId);
305
307
  return {
306
308
  source: {
@@ -368,6 +370,7 @@ export const autoplanWorkflow = defineWorkflow({
368
370
  statusDetail: "capturing the user's instructions",
369
371
  prompt: () =>
370
372
  [
373
+ "Continue in this Pi session. Do not delegate this workflow step or start another session.",
371
374
  "Read the conversation that came before this workflow step.",
372
375
  "Return one text string named originalUserInstructions.",
373
376
  "Include everything that the user has instructed for the intended purpose in the given context.",
@@ -398,12 +401,13 @@ export const autoplanWorkflow = defineWorkflow({
398
401
  expectedOutput: `{ "problem": "concise statement", "success": ["criterion"], "inScope": ["change"], "outOfScope": ["change"], "constraints": ["constraint"], "controlBoundary": "what can change" }`,
399
402
  validate: (value) => requireRecord(value, "autoplan frame"),
400
403
  }),
401
- propose: agent({
402
- statusDetail: "devising candidate solutions",
404
+ solutions: agent({
405
+ statusDetail: "devising long-term solutions",
403
406
  prompt: ({ outputs, input }) =>
404
407
  [
405
408
  originalInstructionsPrompt(outputs),
406
409
  "Give two to four practical options that fit the allowed scope.",
410
+ "For each option, ask: Is this a Long term elegant and production ready solution?",
407
411
  "For each option return a stable lowercase id and a short title. Add a plain gist and full solution, explain the reason and trade-offs, and list the parts.",
408
412
  "Favor a few reusable parts with clear owners and use interfaces that already exist.",
409
413
  "Reject one-off machinery and infrastructure that the task does not need.",
@@ -416,42 +420,46 @@ export const autoplanWorkflow = defineWorkflow({
416
420
  validate: (value, { input }) =>
417
421
  parseProposal(value, (input as AutoplanInput).previousPlan !== undefined),
418
422
  }),
419
- ideal: agent({
420
- statusDetail: "describing the ideal end state",
423
+ holyGrail: agent({
424
+ statusDetail: "describing the Holy grail",
421
425
  prompt: ({ outputs, input }) =>
422
426
  [
423
427
  originalInstructionsPrompt(outputs),
424
- "Describe the best possible end state separately from the practical options.",
425
- "It may match one option or go beyond the current scope.",
428
+ "Describe the Holy grail separately from the practical options.",
429
+ "Ask: Is this the Holy grail for the problem?",
430
+ "The Holy grail may match one option or go beyond the current scope.",
431
+ "Explain what makes it more Long term elegant and production ready than the practical options.",
426
432
  "List each dependency we do not control. Do not assume that it can change.",
427
- "State what this end state would improve beyond the practical options.",
433
+ "State what the Holy grail would improve beyond the practical options.",
428
434
  `Problem frame: ${JSON.stringify(outputs.frame)}`,
429
- `Candidates: ${JSON.stringify(outputs.propose)}`,
435
+ `Candidates: ${JSON.stringify(outputs.solutions)}`,
430
436
  `New evidence: ${JSON.stringify((input as AutoplanInput).newEvidence ?? null)}`,
431
437
  ].join("\n"),
432
- expectedOutput: `{ "ideal": "ideal end state", "outsideDependencies": ["dependency"], "additionalValue": ["benefit"] }`,
438
+ expectedOutput: `{ "ideal": "Holy grail end state", "outsideDependencies": ["dependency"], "additionalValue": ["benefit"] }`,
433
439
  validate: parseIdeal,
434
440
  }),
435
- choose: agent({
436
- statusDetail: "choosing the practical solution",
441
+ select: agent({
442
+ statusDetail: "selecting the solution",
437
443
  prompt: ({ outputs }) =>
438
444
  [
439
445
  originalInstructionsPrompt(outputs),
440
446
  "Select one option. Do not ask the user to choose.",
441
- "Select the ideal only when it fits the allowed scope and is ready for production. Its value must justify the added complexity.",
442
- "Otherwise select the best option we can build now that still moves toward the ideal.",
447
+ "Select the most Long term elegant and production ready option that is proportionate, in scope, and implementable through interfaces we control.",
448
+ "Select the Holy grail when it meets those conditions and its value justifies the added complexity.",
449
+ "Otherwise select the strongest practical option we can build now with a clear path toward the Holy grail.",
443
450
  "Work outside our control does not by itself make the plan blocked.",
444
451
  "Do not require changes to an upstream project or an unrelated repository. Do not require a new service or resource without approval.",
445
452
  "When two options solve the problem equally well, choose the simpler one.",
446
- "Give one specific rejection reason for every option you do not select, including the ideal.",
453
+ "Give one specific rejection reason for every option you do not select, including the Holy grail.",
447
454
  "Return blocked only if no option inside the allowed scope can meet the success criteria.",
448
455
  `Frame: ${JSON.stringify(outputs.frame)}`,
449
- `Candidates: ${JSON.stringify(outputs.propose)}`,
450
- `Ideal candidate id: ideal`,
451
- `Ideal: ${JSON.stringify(outputs.ideal)}`,
456
+ `Candidates: ${JSON.stringify(outputs.solutions)}`,
457
+ `Holy grail candidate id: ideal`,
458
+ `Holy grail: ${JSON.stringify(outputs.holyGrail)}`,
452
459
  ].join("\n"),
453
460
  expectedOutput: `{ "status": "ready" | "blocked", "selectedId": "candidate-id-or-ideal", "why": "reason", "relationshipToIdeal": "relationship", "rejected": [{ "id": "other-id", "reason": "why it lost" }], "compromises": ["compromise"], "blocker": "required only when blocked" }`,
454
- validate: (value, { outputs }) => parseSelection(value, outputs.propose as AutoplanProposal),
461
+ validate: (value, { outputs }) =>
462
+ parseSelection(value, outputs.solutions as AutoplanProposal),
455
463
  }),
456
464
  plan: agent({
457
465
  timeoutMs: 30 * 60_000,
@@ -466,8 +474,8 @@ export const autoplanWorkflow = defineWorkflow({
466
474
  "Correct the earlier plan when the new evidence proves it wrong.",
467
475
  "Do not change files.",
468
476
  `Frame: ${JSON.stringify(outputs.frame)}`,
469
- `Selection: ${JSON.stringify(outputs.choose)}`,
470
- `Candidates: ${JSON.stringify(outputs.propose)}`,
477
+ `Selection: ${JSON.stringify(outputs.select)}`,
478
+ `Candidates: ${JSON.stringify(outputs.solutions)}`,
471
479
  `Previous plan: ${JSON.stringify((input as AutoplanInput).previousPlan ?? null)}`,
472
480
  `New evidence: ${JSON.stringify((input as AutoplanInput).newEvidence ?? null)}`,
473
481
  ].join("\n"),
@@ -476,13 +484,13 @@ export const autoplanWorkflow = defineWorkflow({
476
484
  }),
477
485
  blocked: compute({
478
486
  run: ({ outputs }) => {
479
- const selection = outputs.choose as AutoplanSelection;
487
+ const selection = outputs.select as AutoplanSelection;
480
488
  return {
481
489
  status: "blocked",
482
490
  originalUserInstructions: originalUserInstructions(outputs),
483
491
  frame: outputs.frame,
484
- proposal: outputs.propose as AutoplanProposal,
485
- ideal: outputs.ideal as AutoplanIdeal,
492
+ proposal: outputs.solutions as AutoplanProposal,
493
+ ideal: outputs.holyGrail as AutoplanIdeal,
486
494
  selection,
487
495
  plainSummary: plainSummaryResult(outputs.blockedSummary, "autoplan blocked summary"),
488
496
  reason: requireString(selection.blocker, "autoplan blocker"),
@@ -499,9 +507,9 @@ export const autoplanWorkflow = defineWorkflow({
499
507
  status: "ready",
500
508
  originalUserInstructions: originalUserInstructions(outputs),
501
509
  frame: outputs.frame,
502
- proposal: outputs.propose as AutoplanProposal,
503
- ideal: outputs.ideal as AutoplanIdeal,
504
- selection: outputs.choose as AutoplanSelection,
510
+ proposal: outputs.solutions as AutoplanProposal,
511
+ ideal: outputs.holyGrail as AutoplanIdeal,
512
+ selection: outputs.select as AutoplanSelection,
505
513
  plan: outputs.plan,
506
514
  plainSummary: plainSummaryResult(outputs.readySummary, "autoplan ready summary"),
507
515
  planDigest,
@@ -513,11 +521,11 @@ export const autoplanWorkflow = defineWorkflow({
513
521
  },
514
522
  edges: [
515
523
  { from: "captureIntent", to: "frame" },
516
- { from: "frame", to: "propose" },
517
- { from: "propose", to: "ideal" },
518
- { from: "ideal", to: "choose" },
524
+ { from: "frame", to: "solutions" },
525
+ { from: "solutions", to: "holyGrail" },
526
+ { from: "holyGrail", to: "select" },
519
527
  {
520
- from: "choose",
528
+ from: "select",
521
529
  switch: { on: "$.status", cases: { ready: "plan", blocked: "blockedSummary" } },
522
530
  },
523
531
  { from: "plan", to: "readySummary" },
@@ -9,9 +9,9 @@ import sanityCheckWorkflow from "./sanity-check.workflow.js";
9
9
 
10
10
  export const builtinWorkflowCatalog = new BuiltinWorkflowCatalog([
11
11
  { id: "plain-summary", revision: "3", definition: plainSummaryWorkflow },
12
- { id: "autoplan", revision: "5", definition: autoplanWorkflow },
12
+ { id: "autoplan", revision: "6", definition: autoplanWorkflow },
13
13
  { id: "autodoc", revision: "2", definition: autodocWorkflow },
14
- { id: "autoimplement", revision: "10", definition: autoimplementWorkflow },
14
+ { id: "autoimplement", revision: "11", definition: autoimplementWorkflow },
15
15
  { id: "plan-approval", revision: "4", definition: planApprovalWorkflow },
16
16
  { id: "sanity-check", revision: "6", definition: sanityCheckWorkflow },
17
17
  {
@@ -49,6 +49,7 @@ export {
49
49
  } from "./workflow-engine-scheduler.js";
50
50
  export {
51
51
  ControllerWorkflowCoordinator,
52
+ type ControllerWorkflowControlRequest,
52
53
  type ControllerWorkflowScheduler,
53
54
  type WorkflowSchedulerRequest,
54
55
  type WorkflowSchedulerResult,
@@ -64,6 +65,11 @@ export type {
64
65
  ControllerConditionStatus,
65
66
  ControllerDefinition,
66
67
  ControllerEffects,
68
+ ControllerFollowUpResult,
69
+ ControllerQueueFollowUpRequest,
70
+ ControllerRemoveFollowUpRequest,
71
+ ControllerSettingsChangeRequest,
72
+ ControllerSettingsChangeResult,
67
73
  ControllerEvent,
68
74
  ControllerQueueClaim,
69
75
  ControllerResource,