@osolmaz/pi-workflows 0.9.1 → 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.
- package/README.md +64 -19
- 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 +353 -0
- package/dist/builtins/autoimplement.workflow.js +961 -0
- package/dist/builtins/autoimplement.workflow.js.map +1 -0
- package/dist/builtins/autoplan.workflow.d.ts +58 -0
- package/dist/builtins/autoplan.workflow.js +190 -0
- package/dist/builtins/autoplan.workflow.js.map +1 -0
- package/dist/builtins/catalog.js +9 -1
- package/dist/builtins/catalog.js.map +1 -1
- package/dist/builtins/index.d.ts +6 -0
- package/dist/builtins/index.js +7 -0
- package/dist/builtins/index.js.map +1 -0
- package/dist/builtins/monitor.workflow.d.ts +29 -3
- package/dist/builtins/monitor.workflow.js +325 -13
- 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/host/rpc-bridge.js +4 -21
- package/dist/host/rpc-bridge.js.map +1 -1
- package/dist/render/graph-render.js +46 -10
- package/dist/render/graph-render.js.map +1 -1
- package/dist/viewer/render.js +36 -2
- package/dist/viewer/render.js.map +1 -1
- package/dist/workflows/catalog.d.ts +1 -0
- package/dist/workflows/catalog.js +6 -0
- package/dist/workflows/catalog.js.map +1 -1
- package/dist/workflows/composition.d.ts +45 -0
- package/dist/workflows/composition.js +485 -0
- package/dist/workflows/composition.js.map +1 -0
- 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/decision.d.ts +11 -5
- package/dist/workflows/decision.js.map +1 -1
- package/dist/workflows/definition.d.ts +22 -3
- package/dist/workflows/definition.js +46 -3
- package/dist/workflows/definition.js.map +1 -1
- package/dist/workflows/engine.d.ts +2 -1
- package/dist/workflows/engine.js +199 -18
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/graph.js +8 -6
- package/dist/workflows/graph.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 +5 -2
- package/dist/workflows/index.js +4 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/loader.d.ts +5 -4
- package/dist/workflows/loader.js +118 -18
- package/dist/workflows/loader.js.map +1 -1
- package/dist/workflows/schema.d.ts +3 -1
- package/dist/workflows/schema.js +64 -2
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.js +39 -2
- 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 +300 -2
- package/docs/CONTROLLERS.md +1 -1
- package/docs/DESIGN_PHILOSOPHY.md +1 -1
- package/docs/HUMAN_DECISIONS.md +371 -0
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +323 -0
- package/docs/MONITOR.md +46 -18
- package/docs/WORKFLOW_COMPOSITION.md +333 -0
- 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 +308 -0
- package/docs/run-bundles.md +45 -10
- package/docs/workflows.md +106 -15
- package/examples/workflows/approved-plan.workflow.ts +58 -0
- package/examples/workflows/autoimplement.workflow.ts +1 -92
- 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 +6 -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 +8 -1
- package/skills/pi-workflows/SKILL.md +5 -3
- package/src/builtins/autodoc.workflow.ts +325 -0
- package/src/builtins/autoimplement.workflow.ts +1123 -0
- package/src/builtins/autoplan.workflow.ts +231 -0
- package/src/builtins/catalog.ts +9 -1
- package/src/builtins/index.ts +32 -0
- package/src/builtins/monitor.workflow.ts +396 -15
- 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/host/rpc-bridge.ts +7 -30
- package/src/render/graph-render.ts +54 -5
- package/src/viewer/render.ts +56 -3
- package/src/workflows/catalog.ts +7 -0
- package/src/workflows/composition.ts +642 -0
- package/src/workflows/decision-presentation.ts +501 -0
- package/src/workflows/decision.ts +12 -5
- package/src/workflows/definition.ts +118 -8
- package/src/workflows/engine.ts +257 -20
- package/src/workflows/graph.ts +8 -6
- package/src/workflows/human-decision.ts +819 -0
- package/src/workflows/index.ts +89 -0
- package/src/workflows/loader.ts +186 -18
- package/src/workflows/schema.ts +79 -2
- package/src/workflows/store.ts +45 -2
- package/src/workflows/tool-input.ts +202 -0
- package/src/workflows/types.ts +374 -2
- package/examples/workflows/elegant-solution.workflow.ts +0 -95
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { compute, defineWorkflow } from "../workflows/definition.js";
|
|
2
|
+
import {
|
|
3
|
+
choice,
|
|
4
|
+
defineHumanChoices,
|
|
5
|
+
humanDecision,
|
|
6
|
+
humanDecisionEdge,
|
|
7
|
+
textInput,
|
|
8
|
+
} from "../workflows/human-decision.js";
|
|
9
|
+
import type { HumanDecisionReceipt, HumanDecisionResponse } from "../workflows/types.js";
|
|
10
|
+
import { presentPlan } from "./plan-presentation.js";
|
|
11
|
+
|
|
12
|
+
export type PlanApprovalInput = {
|
|
13
|
+
task: string;
|
|
14
|
+
plan: unknown;
|
|
15
|
+
planDigest: string;
|
|
16
|
+
audience: string;
|
|
17
|
+
revision?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type PlanApprovalContinue = {
|
|
21
|
+
status: "continue";
|
|
22
|
+
plan: unknown;
|
|
23
|
+
planDigest: string;
|
|
24
|
+
decision: HumanDecisionReceipt;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type PlanApprovalStop = {
|
|
28
|
+
status: "stop";
|
|
29
|
+
planDigest: string;
|
|
30
|
+
decision: HumanDecisionReceipt;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type PlanApprovalReplan = {
|
|
34
|
+
status: "replan";
|
|
35
|
+
plan: unknown;
|
|
36
|
+
planDigest: string;
|
|
37
|
+
instructions: string;
|
|
38
|
+
decision: HumanDecisionReceipt;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const planChoices = defineHumanChoices({
|
|
42
|
+
continue: choice({ label: "Yes, continue" }),
|
|
43
|
+
stop: choice({ label: "No, stop" }),
|
|
44
|
+
replan: choice({
|
|
45
|
+
label: "Replan",
|
|
46
|
+
input: textInput({
|
|
47
|
+
name: "instructions",
|
|
48
|
+
prompt: "What should change?",
|
|
49
|
+
minLength: 1,
|
|
50
|
+
maxLength: 4_000,
|
|
51
|
+
}),
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function requireRecord(value: unknown, label: string): Record<string, unknown> {
|
|
56
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
57
|
+
throw new Error(`${label} must be an object`);
|
|
58
|
+
}
|
|
59
|
+
return value as Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function requireString(value: unknown, label: string): string {
|
|
63
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
64
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
65
|
+
}
|
|
66
|
+
return value.trim();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseInput(value: unknown): PlanApprovalInput {
|
|
70
|
+
const input = requireRecord(value, "plan approval input");
|
|
71
|
+
const revision = input.revision;
|
|
72
|
+
if (revision !== undefined && (!Number.isInteger(revision) || (revision as number) < 1)) {
|
|
73
|
+
throw new Error("plan approval revision must be a positive integer");
|
|
74
|
+
}
|
|
75
|
+
if (input.plan === undefined) throw new Error("plan approval requires a plan");
|
|
76
|
+
return {
|
|
77
|
+
task: requireString(input.task, "plan approval task"),
|
|
78
|
+
plan: input.plan,
|
|
79
|
+
planDigest: requireString(input.planDigest, "plan approval digest"),
|
|
80
|
+
audience: requireString(input.audience, "plan approval audience"),
|
|
81
|
+
...(revision !== undefined ? { revision: revision as number } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function acceptedDecision(value: HumanDecisionReceipt | undefined): HumanDecisionReceipt {
|
|
86
|
+
if (value === undefined)
|
|
87
|
+
throw new Error("plan approval continuation is missing its accepted decision");
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function gateResponse(value: unknown): HumanDecisionResponse {
|
|
92
|
+
return requireRecord(value, "plan approval response") as HumanDecisionResponse;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const planApprovalWorkflow = defineWorkflow({
|
|
96
|
+
source: import.meta.url,
|
|
97
|
+
contractId: "pi-workflows.plan-approval.v1",
|
|
98
|
+
name: "plan-approval",
|
|
99
|
+
input: parseInput,
|
|
100
|
+
title: ({ input }) => `plan approval: ${input.task.slice(0, 60)}`,
|
|
101
|
+
startAt: "approve",
|
|
102
|
+
maxSteps: 8,
|
|
103
|
+
exits: {
|
|
104
|
+
continue: {
|
|
105
|
+
from: "continued",
|
|
106
|
+
validate: (value: unknown): PlanApprovalContinue => value as PlanApprovalContinue,
|
|
107
|
+
},
|
|
108
|
+
stop: {
|
|
109
|
+
from: "stopped",
|
|
110
|
+
validate: (value: unknown): PlanApprovalStop => value as PlanApprovalStop,
|
|
111
|
+
},
|
|
112
|
+
replan: {
|
|
113
|
+
from: "replan",
|
|
114
|
+
validate: (value: unknown): PlanApprovalReplan => value as PlanApprovalReplan,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
nodes: {
|
|
118
|
+
approve: humanDecision({
|
|
119
|
+
audience: ({ input }) => (input as PlanApprovalInput).audience,
|
|
120
|
+
choices: planChoices,
|
|
121
|
+
request: ({ input }) => {
|
|
122
|
+
const request = input as PlanApprovalInput;
|
|
123
|
+
const revision = request.revision ?? 1;
|
|
124
|
+
return {
|
|
125
|
+
title: "Approve the implementation plan",
|
|
126
|
+
subject: {
|
|
127
|
+
task: request.task,
|
|
128
|
+
plan: request.plan,
|
|
129
|
+
planDigest: request.planDigest,
|
|
130
|
+
revision,
|
|
131
|
+
},
|
|
132
|
+
presentation: presentPlan({
|
|
133
|
+
task: request.task,
|
|
134
|
+
plan: request.plan,
|
|
135
|
+
planDigest: request.planDigest,
|
|
136
|
+
revision,
|
|
137
|
+
}),
|
|
138
|
+
revision,
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
continued: compute({
|
|
143
|
+
run: ({ input, state }) => {
|
|
144
|
+
const request = input as PlanApprovalInput;
|
|
145
|
+
return {
|
|
146
|
+
status: "continue",
|
|
147
|
+
plan: request.plan,
|
|
148
|
+
planDigest: request.planDigest,
|
|
149
|
+
decision: acceptedDecision(state.humanDecision),
|
|
150
|
+
} satisfies PlanApprovalContinue;
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
stopped: compute({
|
|
154
|
+
run: ({ input, state }) => {
|
|
155
|
+
const request = input as PlanApprovalInput;
|
|
156
|
+
return {
|
|
157
|
+
status: "stop",
|
|
158
|
+
planDigest: request.planDigest,
|
|
159
|
+
decision: acceptedDecision(state.humanDecision),
|
|
160
|
+
} satisfies PlanApprovalStop;
|
|
161
|
+
},
|
|
162
|
+
}),
|
|
163
|
+
replan: compute({
|
|
164
|
+
run: ({ input, outputs, state }) => {
|
|
165
|
+
const request = input as PlanApprovalInput;
|
|
166
|
+
const response = gateResponse(outputs.approve);
|
|
167
|
+
const instructions = response.input?.instructions;
|
|
168
|
+
if (typeof instructions !== "string" || instructions.length === 0) {
|
|
169
|
+
throw new Error("replan answer is missing exact instructions");
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
status: "replan",
|
|
173
|
+
plan: request.plan,
|
|
174
|
+
planDigest: request.planDigest,
|
|
175
|
+
instructions,
|
|
176
|
+
decision: acceptedDecision(state.humanDecision),
|
|
177
|
+
} satisfies PlanApprovalReplan;
|
|
178
|
+
},
|
|
179
|
+
}),
|
|
180
|
+
},
|
|
181
|
+
edges: [
|
|
182
|
+
humanDecisionEdge({
|
|
183
|
+
from: "approve",
|
|
184
|
+
choices: planChoices,
|
|
185
|
+
cases: { continue: "continued", stop: "stopped", replan: "replan" },
|
|
186
|
+
}),
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
export default planApprovalWorkflow;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
legacyDecisionPresentation,
|
|
3
|
+
normalizeDecisionPresentation,
|
|
4
|
+
} from "../workflows/decision-presentation.js";
|
|
5
|
+
import type { DecisionPresentation } from "../workflows/types.js";
|
|
6
|
+
|
|
7
|
+
export function presentPlan(input: {
|
|
8
|
+
task: string;
|
|
9
|
+
plan: unknown;
|
|
10
|
+
planDigest: string;
|
|
11
|
+
revision: number;
|
|
12
|
+
}): DecisionPresentation {
|
|
13
|
+
const plan = legacyDecisionPresentation(input.plan);
|
|
14
|
+
const summary =
|
|
15
|
+
readableSummary(planSummary(input.plan)) ??
|
|
16
|
+
readableSummary(`Review the implementation plan for ${input.task}.`)!;
|
|
17
|
+
return normalizeDecisionPresentation({
|
|
18
|
+
schema: "pi-workflows.decision-presentation.v1",
|
|
19
|
+
summary,
|
|
20
|
+
blocks: [
|
|
21
|
+
{ kind: "section", title: "Task" },
|
|
22
|
+
{ kind: "paragraph", text: input.task },
|
|
23
|
+
{ kind: "section", title: "Plan" },
|
|
24
|
+
...plan.blocks.filter(
|
|
25
|
+
(block, index) =>
|
|
26
|
+
!(index === 0 && block.kind === "section" && block.title === "Decision details"),
|
|
27
|
+
),
|
|
28
|
+
{ kind: "section", title: "Plan identity" },
|
|
29
|
+
{
|
|
30
|
+
kind: "fields",
|
|
31
|
+
items: [
|
|
32
|
+
{ label: "Revision", value: String(input.revision) },
|
|
33
|
+
{ label: "Digest", value: input.planDigest },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readableSummary(value: string | undefined): string | undefined {
|
|
41
|
+
if (value === undefined) return undefined;
|
|
42
|
+
const text = [...value]
|
|
43
|
+
.map((character) => {
|
|
44
|
+
const code = character.codePointAt(0) ?? 0;
|
|
45
|
+
return code <= 31 || code === 127 ? " " : character;
|
|
46
|
+
})
|
|
47
|
+
.join("")
|
|
48
|
+
.replaceAll(/\s+/gu, " ")
|
|
49
|
+
.trim();
|
|
50
|
+
return text.length === 0 ? undefined : text;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function planSummary(plan: unknown): string | undefined {
|
|
54
|
+
if (plan === null || typeof plan !== "object" || Array.isArray(plan)) return undefined;
|
|
55
|
+
const summary = (plan as Record<string, unknown>).summary;
|
|
56
|
+
return typeof summary === "string" && summary.trim().length > 0 ? summary : undefined;
|
|
57
|
+
}
|