@osolmaz/pi-workflows 0.13.0 → 0.13.2

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 (84) hide show
  1. package/README.md +11 -9
  2. package/dist/builtins/autodoc.workflow.d.ts +191 -4
  3. package/dist/builtins/autodoc.workflow.js +156 -30
  4. package/dist/builtins/autodoc.workflow.js.map +1 -1
  5. package/dist/builtins/autoimplement-command-batches.d.ts +1 -0
  6. package/dist/builtins/autoimplement-command-batches.js +29 -31
  7. package/dist/builtins/autoimplement-command-batches.js.map +1 -1
  8. package/dist/builtins/autoimplement.workflow.d.ts +1259 -29
  9. package/dist/builtins/autoimplement.workflow.js +416 -153
  10. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  11. package/dist/builtins/autoplan.workflow.d.ts +6 -0
  12. package/dist/builtins/autoplan.workflow.js +68 -32
  13. package/dist/builtins/autoplan.workflow.js.map +1 -1
  14. package/dist/builtins/catalog.js +5 -5
  15. package/dist/builtins/catalog.js.map +1 -1
  16. package/dist/builtins/change-verification.workflow.d.ts +110 -0
  17. package/dist/builtins/change-verification.workflow.js +860 -0
  18. package/dist/builtins/change-verification.workflow.js.map +1 -0
  19. package/dist/builtins/monitor.workflow.js +4 -3
  20. package/dist/builtins/monitor.workflow.js.map +1 -1
  21. package/dist/builtins/plain-summary.workflow.js +35 -24
  22. package/dist/builtins/plain-summary.workflow.js.map +1 -1
  23. package/dist/builtins/plan-change.workflow.d.ts +361 -6
  24. package/dist/builtins/plan-change.workflow.js +26 -0
  25. package/dist/builtins/plan-change.workflow.js.map +1 -1
  26. package/dist/builtins/sanity-check.workflow.js +19 -22
  27. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  28. package/dist/builtins/workspace-preparation.workflow.d.ts +75 -0
  29. package/dist/builtins/workspace-preparation.workflow.js +498 -0
  30. package/dist/builtins/workspace-preparation.workflow.js.map +1 -0
  31. package/dist/controllers/sqlite.js +16 -51
  32. package/dist/controllers/sqlite.js.map +1 -1
  33. package/dist/extension/decision-channels.js +45 -7
  34. package/dist/extension/decision-channels.js.map +1 -1
  35. package/dist/extension/executor.js +1 -4
  36. package/dist/extension/executor.js.map +1 -1
  37. package/dist/extension/index.js +2 -15
  38. package/dist/extension/index.js.map +1 -1
  39. package/dist/extension/recorder.d.ts +1 -1
  40. package/dist/extension/recorder.js +8 -8
  41. package/dist/extension/recorder.js.map +1 -1
  42. package/dist/state/schema.js +22 -3
  43. package/dist/state/schema.js.map +1 -1
  44. package/dist/viewer/session-reducer.d.ts +0 -1
  45. package/dist/viewer/session-reducer.js +2 -24
  46. package/dist/viewer/session-reducer.js.map +1 -1
  47. package/dist/workflows/engine.js +4 -3
  48. package/dist/workflows/engine.js.map +1 -1
  49. package/dist/workflows/store.d.ts +6 -1
  50. package/dist/workflows/store.js +286 -33
  51. package/dist/workflows/store.js.map +1 -1
  52. package/dist/workflows/types.d.ts +1 -1
  53. package/docs/SQLITE_STATE.md +20 -14
  54. package/docs/WORKFLOW_COMPOSITION.md +8 -0
  55. package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +1 -1
  56. package/docs/plans/2026-08-24-change-scoped-verification-plan.md +419 -0
  57. package/docs/plans/2026-08-25-autoplan-user-intent-capture-plan.md +107 -0
  58. package/docs/session-event-journal.md +2 -3
  59. package/docs/workflows.md +43 -21
  60. package/herdr-plugin.toml +1 -1
  61. package/package.json +1 -1
  62. package/skills/autodoc/SKILL.md +7 -0
  63. package/skills/autoimplement/SKILL.md +4 -0
  64. package/src/builtins/autodoc.workflow.ts +184 -33
  65. package/src/builtins/autoimplement-command-batches.ts +39 -33
  66. package/src/builtins/autoimplement.workflow.ts +483 -175
  67. package/src/builtins/autoplan.workflow.ts +80 -32
  68. package/src/builtins/catalog.ts +5 -5
  69. package/src/builtins/change-verification.workflow.ts +1143 -0
  70. package/src/builtins/monitor.workflow.ts +6 -3
  71. package/src/builtins/plain-summary.workflow.ts +38 -40
  72. package/src/builtins/plan-change.workflow.ts +35 -0
  73. package/src/builtins/sanity-check.workflow.ts +19 -22
  74. package/src/builtins/workspace-preparation.workflow.ts +668 -0
  75. package/src/controllers/sqlite.ts +19 -53
  76. package/src/extension/decision-channels.ts +47 -7
  77. package/src/extension/executor.ts +1 -4
  78. package/src/extension/index.ts +2 -15
  79. package/src/extension/recorder.ts +10 -8
  80. package/src/state/schema.ts +22 -3
  81. package/src/viewer/session-reducer.ts +2 -29
  82. package/src/workflows/engine.ts +4 -3
  83. package/src/workflows/store.ts +397 -43
  84. package/src/workflows/types.ts +0 -1
@@ -46,8 +46,13 @@ export type AutoplanSelection = {
46
46
  blocker?: string;
47
47
  };
48
48
 
49
+ export type AutoplanIntent = {
50
+ originalUserInstructions: string;
51
+ };
52
+
49
53
  export type AutoplanReady = {
50
54
  status: "ready";
55
+ originalUserInstructions: string;
51
56
  frame: unknown;
52
57
  proposal: AutoplanProposal;
53
58
  ideal: AutoplanIdeal;
@@ -61,6 +66,7 @@ export type AutoplanReady = {
61
66
 
62
67
  export type AutoplanBlocked = {
63
68
  status: "blocked";
69
+ originalUserInstructions: string;
64
70
  frame: unknown;
65
71
  proposal: AutoplanProposal;
66
72
  ideal: AutoplanIdeal;
@@ -76,6 +82,25 @@ function requireRecord(value: unknown, label: string): Record<string, unknown> {
76
82
  return value as Record<string, unknown>;
77
83
  }
78
84
 
85
+ function parseIntent(value: unknown): AutoplanIntent {
86
+ const intent = requireRecord(value, "autoplan user intent");
87
+ const instructions = intent.originalUserInstructions;
88
+ if (typeof instructions !== "string" || instructions.trim().length === 0) {
89
+ throw new Error("autoplan originalUserInstructions must be a non-empty string");
90
+ }
91
+ return { originalUserInstructions: instructions };
92
+ }
93
+
94
+ function originalUserInstructions(outputs: Record<string, unknown>): string {
95
+ return parseIntent(outputs.captureIntent).originalUserInstructions;
96
+ }
97
+
98
+ function originalInstructionsPrompt(outputs: Record<string, unknown>): string {
99
+ return ["Original user instructions (authoritative):", originalUserInstructions(outputs)].join(
100
+ "\n",
101
+ );
102
+ }
103
+
79
104
  function requireString(value: unknown, label: string): string {
80
105
  if (typeof value !== "string" || value.trim().length === 0) {
81
106
  throw new Error(`${label} must be a non-empty string`);
@@ -279,6 +304,7 @@ function summaryInput(
279
304
  const selected = candidateSummary(proposal, ideal, selection.selectedId);
280
305
  return {
281
306
  source: {
307
+ originalUserInstructions: originalUserInstructions(outputs),
282
308
  status: selection.status,
283
309
  selected,
284
310
  why: selection.why,
@@ -307,8 +333,6 @@ function summaryInput(
307
333
  : []),
308
334
  ...(blocked ? [selection.blocker as string] : ["The plan is selected for approval"]),
309
335
  ],
310
- maxChars: 2_500,
311
- maxSentences: 12,
312
336
  format: "mixed",
313
337
  };
314
338
  }
@@ -319,7 +343,7 @@ export const autoplanWorkflow = defineWorkflow({
319
343
  name: "autoplan",
320
344
  input: parseInput,
321
345
  title: ({ input }) => `autoplan: ${input.problem.slice(0, 60)}`,
322
- startAt: "frame",
346
+ startAt: "captureIntent",
323
347
  maxSteps: 16,
324
348
  includes: {
325
349
  readySummary: includeWorkflow(plainSummaryWorkflow, {
@@ -340,18 +364,35 @@ export const autoplanWorkflow = defineWorkflow({
340
364
  },
341
365
  },
342
366
  nodes: {
367
+ captureIntent: agent({
368
+ statusDetail: "capturing the user's instructions",
369
+ prompt: () =>
370
+ [
371
+ "Read the conversation that came before this workflow step.",
372
+ "Return one text string named originalUserInstructions.",
373
+ "Include everything that the user has instructed for the intended purpose in the given context.",
374
+ "Include relevant earlier or queued user messages that are present in the context.",
375
+ "When several messages contribute, preserve their wording and chronological order in the one text value.",
376
+ "Do not summarize, rewrite, explain, label, omit, or add instructions.",
377
+ "Do not return an array or message objects.",
378
+ ].join("\n"),
379
+ expectedOutput: `{ "originalUserInstructions": "all relevant user instructions in one text string" }`,
380
+ validate: parseIntent,
381
+ }),
343
382
  frame: agent({
344
383
  statusDetail: "framing the problem",
345
- prompt: ({ input }) => {
384
+ prompt: ({ outputs, input }) => {
346
385
  const request = input as AutoplanInput;
347
386
  return [
348
- `Frame this problem: ${request.problem}`,
349
- `Authorized scope: ${request.scope ?? "infer it conservatively from the request and current project"}.`,
387
+ originalInstructionsPrompt(outputs),
388
+ `Caller-provided problem description (supplemental): ${request.problem}`,
389
+ `Allowed scope: ${request.scope ?? "infer it conservatively from the request and current project"}.`,
350
390
  `Constraints: ${JSON.stringify(request.constraints ?? [])}.`,
351
391
  `Previous plan: ${JSON.stringify(request.previousPlan ?? null)}.`,
352
392
  `New evidence: ${JSON.stringify(request.newEvidence ?? null)}.`,
353
- "Identify the goal, observable success criteria, systems in scope, systems outside scope, and interfaces we control.",
354
- "Do not invent permission to change an upstream project, external service, or unrelated repository.",
393
+ "State the goal and describe what success looks like.",
394
+ "List the systems we may change and the systems we must leave alone. Name the interfaces we control.",
395
+ "Do not assume permission to change an upstream project, an external service, or an unrelated repository.",
355
396
  ].join("\n");
356
397
  },
357
398
  expectedOutput: `{ "problem": "concise statement", "success": ["criterion"], "inScope": ["change"], "outOfScope": ["change"], "constraints": ["constraint"], "controlBoundary": "what can change" }`,
@@ -361,12 +402,13 @@ export const autoplanWorkflow = defineWorkflow({
361
402
  statusDetail: "devising candidate solutions",
362
403
  prompt: ({ outputs, input }) =>
363
404
  [
364
- "Devise two through four distinct practical solutions within the framed scope.",
365
- "Each candidate needs a stable lowercase id, short title, plain gist, full solution, rationale, parts, and trade-offs.",
366
- "Prefer a small number of general parts, clear ownership boundaries, and existing public interfaces.",
367
- "Avoid one-off mechanisms and unnecessary infrastructure.",
368
- "When a previous plan exists, mark it as a candidate or reject it with a reason.",
369
- "Do not implement anything.",
405
+ originalInstructionsPrompt(outputs),
406
+ "Give two to four practical options that fit the allowed scope.",
407
+ "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
+ "Favor a few reusable parts with clear owners and use interfaces that already exist.",
409
+ "Reject one-off machinery and infrastructure that the task does not need.",
410
+ "If the input includes an earlier plan, keep it as an option or explain why the new evidence rules it out.",
411
+ "Do not change files.",
370
412
  `Problem frame: ${JSON.stringify(outputs.frame)}`,
371
413
  `Previous plan: ${JSON.stringify((input as AutoplanInput).previousPlan ?? null)}`,
372
414
  ].join("\n"),
@@ -378,10 +420,11 @@ export const autoplanWorkflow = defineWorkflow({
378
420
  statusDetail: "describing the ideal end state",
379
421
  prompt: ({ outputs, input }) =>
380
422
  [
381
- "Set the practical candidates aside and describe the holy grail for this problem.",
382
- "The holy grail can match a candidate or exceed the current scope.",
383
- "Name dependencies outside our authority instead of assuming they can change.",
384
- "Explain the practical value beyond the candidates.",
423
+ 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.",
426
+ "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.",
385
428
  `Problem frame: ${JSON.stringify(outputs.frame)}`,
386
429
  `Candidates: ${JSON.stringify(outputs.propose)}`,
387
430
  `New evidence: ${JSON.stringify((input as AutoplanInput).newEvidence ?? null)}`,
@@ -393,14 +436,15 @@ export const autoplanWorkflow = defineWorkflow({
393
436
  statusDetail: "choosing the practical solution",
394
437
  prompt: ({ outputs }) =>
395
438
  [
396
- "Choose the right solution without asking the user to decide.",
397
- "Choose the ideal when it is production-ready, proportionate, in scope, and implementable through interfaces we control.",
398
- "Otherwise choose the strongest practical in-scope candidate with a clear path toward the ideal.",
399
- "Do not block only because the ideal depends on work outside our authority.",
400
- "Do not make an upstream change, unrelated repository, new service, or unapproved resource a requirement.",
401
- "Prefer the simpler choice when options give materially equivalent results.",
402
- "Return one rejection reason for every non-selected candidate, including the ideal when it is not selected.",
403
- "Return blocked only when no truthful in-scope solution can meet the success criteria.",
439
+ originalInstructionsPrompt(outputs),
440
+ "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.",
443
+ "Work outside our control does not by itself make the plan blocked.",
444
+ "Do not require changes to an upstream project or an unrelated repository. Do not require a new service or resource without approval.",
445
+ "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.",
447
+ "Return blocked only if no option inside the allowed scope can meet the success criteria.",
404
448
  `Frame: ${JSON.stringify(outputs.frame)}`,
405
449
  `Candidates: ${JSON.stringify(outputs.propose)}`,
406
450
  `Ideal candidate id: ideal`,
@@ -414,12 +458,13 @@ export const autoplanWorkflow = defineWorkflow({
414
458
  statusDetail: "writing the implementation plan",
415
459
  prompt: ({ outputs, input }) =>
416
460
  [
417
- "Write a detailed implementation-ready plan for the selected solution.",
418
- "Keep every step inside the framed scope and authority.",
419
- "For each step, state what changes, where it changes, and how to verify it.",
420
- "Include contract changes, compatibility boundaries, tests, rollout or migration work, and failure handling when they apply.",
421
- "Use the new evidence to correct the previous plan when one exists.",
422
- "Do not implement the plan.",
461
+ originalInstructionsPrompt(outputs),
462
+ "Turn the selected option into a plan that another engineer can implement.",
463
+ "Keep every step inside the allowed scope and authority.",
464
+ "For each step name the location and exact change before stating the check that proves it works.",
465
+ "Describe contract changes and compatibility boundaries before listing tests. Include rollout, migration, and failure handling only where they apply.",
466
+ "Correct the earlier plan when the new evidence proves it wrong.",
467
+ "Do not change files.",
423
468
  `Frame: ${JSON.stringify(outputs.frame)}`,
424
469
  `Selection: ${JSON.stringify(outputs.choose)}`,
425
470
  `Candidates: ${JSON.stringify(outputs.propose)}`,
@@ -434,6 +479,7 @@ export const autoplanWorkflow = defineWorkflow({
434
479
  const selection = outputs.choose as AutoplanSelection;
435
480
  return {
436
481
  status: "blocked",
482
+ originalUserInstructions: originalUserInstructions(outputs),
437
483
  frame: outputs.frame,
438
484
  proposal: outputs.propose as AutoplanProposal,
439
485
  ideal: outputs.ideal as AutoplanIdeal,
@@ -451,6 +497,7 @@ export const autoplanWorkflow = defineWorkflow({
451
497
  request.previousPlan === undefined ? undefined : digest(request.previousPlan);
452
498
  return {
453
499
  status: "ready",
500
+ originalUserInstructions: originalUserInstructions(outputs),
454
501
  frame: outputs.frame,
455
502
  proposal: outputs.propose as AutoplanProposal,
456
503
  ideal: outputs.ideal as AutoplanIdeal,
@@ -465,6 +512,7 @@ export const autoplanWorkflow = defineWorkflow({
465
512
  }),
466
513
  },
467
514
  edges: [
515
+ { from: "captureIntent", to: "frame" },
468
516
  { from: "frame", to: "propose" },
469
517
  { from: "propose", to: "ideal" },
470
518
  { from: "ideal", to: "choose" },
@@ -8,12 +8,12 @@ import planApprovalWorkflow from "./plan-approval.workflow.js";
8
8
  import sanityCheckWorkflow from "./sanity-check.workflow.js";
9
9
 
10
10
  export const builtinWorkflowCatalog = new BuiltinWorkflowCatalog([
11
- { id: "plain-summary", revision: "1", definition: plainSummaryWorkflow },
12
- { id: "autoplan", revision: "2", definition: autoplanWorkflow },
13
- { id: "autodoc", revision: "1", definition: autodocWorkflow },
14
- { id: "autoimplement", revision: "9", definition: autoimplementWorkflow },
11
+ { id: "plain-summary", revision: "3", definition: plainSummaryWorkflow },
12
+ { id: "autoplan", revision: "5", definition: autoplanWorkflow },
13
+ { id: "autodoc", revision: "2", definition: autodocWorkflow },
14
+ { id: "autoimplement", revision: "10", definition: autoimplementWorkflow },
15
15
  { id: "plan-approval", revision: "4", definition: planApprovalWorkflow },
16
- { id: "sanity-check", revision: "4", definition: sanityCheckWorkflow },
16
+ { id: "sanity-check", revision: "6", definition: sanityCheckWorkflow },
17
17
  {
18
18
  id: "monitor",
19
19
  revision: "11",