@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.
Files changed (147) hide show
  1. package/README.md +64 -19
  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 +353 -0
  6. package/dist/builtins/autoimplement.workflow.js +961 -0
  7. package/dist/builtins/autoimplement.workflow.js.map +1 -0
  8. package/dist/builtins/autoplan.workflow.d.ts +58 -0
  9. package/dist/builtins/autoplan.workflow.js +190 -0
  10. package/dist/builtins/autoplan.workflow.js.map +1 -0
  11. package/dist/builtins/catalog.js +9 -1
  12. package/dist/builtins/catalog.js.map +1 -1
  13. package/dist/builtins/index.d.ts +6 -0
  14. package/dist/builtins/index.js +7 -0
  15. package/dist/builtins/index.js.map +1 -0
  16. package/dist/builtins/monitor.workflow.d.ts +29 -3
  17. package/dist/builtins/monitor.workflow.js +325 -13
  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 +46 -10
  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/catalog.d.ts +1 -0
  43. package/dist/workflows/catalog.js +6 -0
  44. package/dist/workflows/catalog.js.map +1 -1
  45. package/dist/workflows/composition.d.ts +45 -0
  46. package/dist/workflows/composition.js +485 -0
  47. package/dist/workflows/composition.js.map +1 -0
  48. package/dist/workflows/decision-presentation.d.ts +18 -0
  49. package/dist/workflows/decision-presentation.js +417 -0
  50. package/dist/workflows/decision-presentation.js.map +1 -0
  51. package/dist/workflows/decision.d.ts +11 -5
  52. package/dist/workflows/decision.js.map +1 -1
  53. package/dist/workflows/definition.d.ts +22 -3
  54. package/dist/workflows/definition.js +46 -3
  55. package/dist/workflows/definition.js.map +1 -1
  56. package/dist/workflows/engine.d.ts +2 -1
  57. package/dist/workflows/engine.js +199 -18
  58. package/dist/workflows/engine.js.map +1 -1
  59. package/dist/workflows/graph.js +8 -6
  60. package/dist/workflows/graph.js.map +1 -1
  61. package/dist/workflows/human-decision.d.ts +87 -0
  62. package/dist/workflows/human-decision.js +583 -0
  63. package/dist/workflows/human-decision.js.map +1 -0
  64. package/dist/workflows/index.d.ts +5 -2
  65. package/dist/workflows/index.js +4 -1
  66. package/dist/workflows/index.js.map +1 -1
  67. package/dist/workflows/loader.d.ts +5 -4
  68. package/dist/workflows/loader.js +118 -18
  69. package/dist/workflows/loader.js.map +1 -1
  70. package/dist/workflows/schema.d.ts +3 -1
  71. package/dist/workflows/schema.js +64 -2
  72. package/dist/workflows/schema.js.map +1 -1
  73. package/dist/workflows/store.js +39 -2
  74. package/dist/workflows/store.js.map +1 -1
  75. package/dist/workflows/tool-input.d.ts +72 -0
  76. package/dist/workflows/tool-input.js +141 -0
  77. package/dist/workflows/tool-input.js.map +1 -0
  78. package/dist/workflows/types.d.ts +300 -2
  79. package/docs/CONTROLLERS.md +1 -1
  80. package/docs/DESIGN_PHILOSOPHY.md +1 -1
  81. package/docs/HUMAN_DECISIONS.md +371 -0
  82. package/docs/HUMAN_DECISION_PRESENTATIONS.md +323 -0
  83. package/docs/MONITOR.md +46 -18
  84. package/docs/WORKFLOW_COMPOSITION.md +333 -0
  85. package/docs/plans/2026-08-19-human-decision-gates-plan.md +296 -0
  86. package/docs/plans/2026-08-19-human-decision-presentations-plan.md +172 -0
  87. package/docs/plans/2026-08-19-provider-compatible-workflow-tool-schema-plan.md +45 -0
  88. package/docs/plans/2026-08-19-workflow-composition-plan.md +308 -0
  89. package/docs/run-bundles.md +45 -10
  90. package/docs/workflows.md +106 -15
  91. package/examples/workflows/approved-plan.workflow.ts +58 -0
  92. package/examples/workflows/autoimplement.workflow.ts +1 -92
  93. package/examples/workflows/autoplan.workflow.ts +1 -0
  94. package/examples/workflows/human-decision.workflow.ts +62 -0
  95. package/herdr-plugin.toml +1 -1
  96. package/package.json +6 -1
  97. package/schemas/decision-presentation-v1.schema.json +83 -0
  98. package/schemas/human-decision-accepted-v1.schema.json +44 -0
  99. package/schemas/human-decision-accepted-v2.schema.json +50 -0
  100. package/schemas/human-decision-answer-attempt-v1.schema.json +37 -0
  101. package/schemas/human-decision-cancellation-v1.schema.json +15 -0
  102. package/schemas/human-decision-continuation-v1.schema.json +16 -0
  103. package/schemas/human-decision-delivery-v1.schema.json +28 -0
  104. package/schemas/human-decision-delivery-v2.schema.json +36 -0
  105. package/schemas/human-decision-receipt-v1.schema.json +33 -0
  106. package/schemas/human-decision-receipt-v2.schema.json +39 -0
  107. package/schemas/human-decision-request-v1.schema.json +59 -0
  108. package/schemas/human-decision-request-v2.schema.json +69 -0
  109. package/schemas/human-decision-resolution-v1.schema.json +27 -0
  110. package/schemas/human-decision-resolution-v2.schema.json +27 -0
  111. package/schemas/human-decision-settlement-v1.schema.json +28 -0
  112. package/skills/autodoc/SKILL.md +43 -0
  113. package/skills/autoimplement/SKILL.md +58 -0
  114. package/skills/autoimplement/agents/openai.yaml +4 -0
  115. package/skills/autoplan/SKILL.md +25 -0
  116. package/skills/monitor/SKILL.md +8 -1
  117. package/skills/pi-workflows/SKILL.md +5 -3
  118. package/src/builtins/autodoc.workflow.ts +325 -0
  119. package/src/builtins/autoimplement.workflow.ts +1123 -0
  120. package/src/builtins/autoplan.workflow.ts +231 -0
  121. package/src/builtins/catalog.ts +9 -1
  122. package/src/builtins/index.ts +32 -0
  123. package/src/builtins/monitor.workflow.ts +396 -15
  124. package/src/builtins/plan-approval.workflow.ts +190 -0
  125. package/src/builtins/plan-presentation.ts +57 -0
  126. package/src/extension/decision-channels.ts +1533 -0
  127. package/src/extension/index.ts +499 -9
  128. package/src/extension/widget.ts +39 -2
  129. package/src/extension/workflow-tool.ts +5 -81
  130. package/src/host/rpc-bridge.ts +7 -30
  131. package/src/render/graph-render.ts +54 -5
  132. package/src/viewer/render.ts +56 -3
  133. package/src/workflows/catalog.ts +7 -0
  134. package/src/workflows/composition.ts +642 -0
  135. package/src/workflows/decision-presentation.ts +501 -0
  136. package/src/workflows/decision.ts +12 -5
  137. package/src/workflows/definition.ts +118 -8
  138. package/src/workflows/engine.ts +257 -20
  139. package/src/workflows/graph.ts +8 -6
  140. package/src/workflows/human-decision.ts +819 -0
  141. package/src/workflows/index.ts +89 -0
  142. package/src/workflows/loader.ts +186 -18
  143. package/src/workflows/schema.ts +79 -2
  144. package/src/workflows/store.ts +45 -2
  145. package/src/workflows/tool-input.ts +202 -0
  146. package/src/workflows/types.ts +374 -2
  147. package/examples/workflows/elegant-solution.workflow.ts +0 -95
@@ -0,0 +1,202 @@
1
+ import { Type, type Static, type TObject, type TProperties, type TSchema } from "typebox";
2
+ import { Parse, ParseError } from "typebox/value";
3
+
4
+ const noExtraProperties = { additionalProperties: false } as const;
5
+
6
+ const offsetSchema = Type.Integer({ minimum: 0, description: "Workflow list offset" });
7
+ const workflowSchema = Type.String({
8
+ description: "Discovered workflow name or workflow file path; required when action is start",
9
+ });
10
+ const inputSchema = Type.Unknown({
11
+ description: "Checkpoint answer for answer; optional structured workflow input for start",
12
+ });
13
+ const runIdSchema = Type.String({
14
+ description: "Run id; optional for status and answer",
15
+ });
16
+ const stepSchema = Type.String({
17
+ description: "Workflow step id; required when action is update or submit",
18
+ });
19
+ const attemptSchema = Type.String({
20
+ description: "Workflow attempt id; required when action is update or submit",
21
+ });
22
+ const updateSchema = Type.Object(
23
+ {
24
+ type: Type.String(),
25
+ key: Type.String(),
26
+ data: Type.Record(Type.String(), Type.Unknown()),
27
+ },
28
+ noExtraProperties,
29
+ );
30
+ const outputSchema = Type.Unknown({
31
+ description: "Step output matching the expected shape; required when action is submit",
32
+ });
33
+
34
+ export const WorkflowActionSchemas = {
35
+ list: Type.Object(
36
+ { action: Type.Literal("list"), offset: Type.Optional(offsetSchema) },
37
+ noExtraProperties,
38
+ ),
39
+ start: Type.Object(
40
+ {
41
+ action: Type.Literal("start"),
42
+ workflow: workflowSchema,
43
+ input: Type.Optional(inputSchema),
44
+ },
45
+ noExtraProperties,
46
+ ),
47
+ status: Type.Object(
48
+ { action: Type.Literal("status"), runId: Type.Optional(runIdSchema) },
49
+ noExtraProperties,
50
+ ),
51
+ pause: Type.Object({ action: Type.Literal("pause") }, noExtraProperties),
52
+ resume: Type.Object({ action: Type.Literal("resume") }, noExtraProperties),
53
+ cancel: Type.Object({ action: Type.Literal("cancel") }, noExtraProperties),
54
+ answer: Type.Object(
55
+ {
56
+ action: Type.Literal("answer"),
57
+ input: inputSchema,
58
+ runId: Type.Optional(runIdSchema),
59
+ },
60
+ noExtraProperties,
61
+ ),
62
+ update: Type.Object(
63
+ {
64
+ action: Type.Literal("update"),
65
+ step: stepSchema,
66
+ attempt: attemptSchema,
67
+ update: updateSchema,
68
+ },
69
+ noExtraProperties,
70
+ ),
71
+ submit: Type.Object(
72
+ {
73
+ action: Type.Literal("submit"),
74
+ step: stepSchema,
75
+ attempt: attemptSchema,
76
+ output: outputSchema,
77
+ },
78
+ noExtraProperties,
79
+ ),
80
+ } as const;
81
+
82
+ const WorkflowSubmissionActionSchemas = {
83
+ update: WorkflowActionSchemas.update,
84
+ submit: WorkflowActionSchemas.submit,
85
+ } as const;
86
+
87
+ type SchemaValue<Schemas extends Record<string, TSchema>> = Schemas[keyof Schemas];
88
+
89
+ export type WorkflowToolInput = Static<SchemaValue<typeof WorkflowActionSchemas>>;
90
+ export type WorkflowSubmissionInput = Static<SchemaValue<typeof WorkflowSubmissionActionSchemas>>;
91
+
92
+ export const WorkflowToolParameters = providerObjectSchema(Object.values(WorkflowActionSchemas));
93
+ export const WorkflowSubmissionToolParameters = providerObjectSchema(
94
+ Object.values(WorkflowSubmissionActionSchemas),
95
+ );
96
+
97
+ type ToolInputParser<Output> = (value: unknown) => Output;
98
+
99
+ const workflowInputParsers = {
100
+ list: (value) => parseToolInput(WorkflowActionSchemas.list, value, "workflow"),
101
+ start: (value) => parseToolInput(WorkflowActionSchemas.start, value, "workflow"),
102
+ status: (value) => parseToolInput(WorkflowActionSchemas.status, value, "workflow"),
103
+ pause: (value) => parseToolInput(WorkflowActionSchemas.pause, value, "workflow"),
104
+ resume: (value) => parseToolInput(WorkflowActionSchemas.resume, value, "workflow"),
105
+ cancel: (value) => parseToolInput(WorkflowActionSchemas.cancel, value, "workflow"),
106
+ answer: (value) => parseToolInput(WorkflowActionSchemas.answer, value, "workflow"),
107
+ update: (value) => parseToolInput(WorkflowActionSchemas.update, value, "workflow"),
108
+ submit: (value) => parseToolInput(WorkflowActionSchemas.submit, value, "workflow"),
109
+ } satisfies Record<keyof typeof WorkflowActionSchemas, ToolInputParser<WorkflowToolInput>>;
110
+
111
+ const workflowSubmissionInputParsers = {
112
+ update: (value) =>
113
+ parseToolInput(WorkflowSubmissionActionSchemas.update, value, "workflow submission"),
114
+ submit: (value) =>
115
+ parseToolInput(WorkflowSubmissionActionSchemas.submit, value, "workflow submission"),
116
+ } satisfies Record<
117
+ keyof typeof WorkflowSubmissionActionSchemas,
118
+ ToolInputParser<WorkflowSubmissionInput>
119
+ >;
120
+
121
+ export function parseWorkflowToolInput(value: unknown): WorkflowToolInput {
122
+ return parseSelectedAction<WorkflowToolInput>(workflowInputParsers, value, "workflow");
123
+ }
124
+
125
+ export function parseWorkflowSubmissionInput(value: unknown): WorkflowSubmissionInput {
126
+ return parseSelectedAction<WorkflowSubmissionInput>(
127
+ workflowSubmissionInputParsers,
128
+ value,
129
+ "workflow submission",
130
+ );
131
+ }
132
+
133
+ function providerObjectSchema(variants: readonly TObject[]): TObject {
134
+ const actions: string[] = [];
135
+ const properties: TProperties = {};
136
+
137
+ for (const variant of variants) {
138
+ const action = variant.properties.action;
139
+ if (!isRecord(action) || typeof action.const !== "string") {
140
+ throw new Error("Workflow action schema must have a string action literal.");
141
+ }
142
+ actions.push(action.const);
143
+
144
+ for (const [name, schema] of Object.entries(variant.properties)) {
145
+ if (name === "action") continue;
146
+ const optionalSchema = Type.Optional(schema);
147
+ const current = properties[name];
148
+ if (current !== undefined && JSON.stringify(current) !== JSON.stringify(optionalSchema)) {
149
+ throw new Error(`Workflow property ${JSON.stringify(name)} has incompatible schemas.`);
150
+ }
151
+ properties[name] = optionalSchema;
152
+ }
153
+ }
154
+
155
+ return Type.Object(
156
+ {
157
+ action: Type.String({ enum: actions }),
158
+ ...properties,
159
+ },
160
+ noExtraProperties,
161
+ );
162
+ }
163
+
164
+ function parseSelectedAction<Output>(
165
+ parsers: Readonly<Record<string, ToolInputParser<Output>>>,
166
+ value: unknown,
167
+ label: string,
168
+ ): Output {
169
+ if (!isRecord(value) || typeof value.action !== "string") throw unknownAction(label);
170
+ const parser = parsers[value.action];
171
+ if (parser === undefined) throw unknownAction(label);
172
+ return parser(value);
173
+ }
174
+
175
+ function unknownAction(label: string): Error {
176
+ return new Error(`Invalid ${label} tool input: action is missing or unknown.`);
177
+ }
178
+
179
+ function isRecord(value: unknown): value is Record<string, unknown> {
180
+ return typeof value === "object" && value !== null && !Array.isArray(value);
181
+ }
182
+
183
+ function parseToolInput<const Schema extends TSchema>(
184
+ schema: Schema,
185
+ value: unknown,
186
+ label: string,
187
+ ): Static<Schema> {
188
+ try {
189
+ return Parse(schema, value);
190
+ } catch (error) {
191
+ if (!(error instanceof ParseError)) throw error;
192
+ const details = error.cause.errors
193
+ .slice(0, 3)
194
+ .map(({ instancePath, message }) => {
195
+ const field = instancePath.replace(/^\//u, "").replaceAll("/", ".");
196
+ const clearMessage = message === "must be integer" ? "must be an integer" : message;
197
+ return `${field ? `${field} ` : ""}${clearMessage}`;
198
+ })
199
+ .join("; ");
200
+ throw new Error(`Invalid ${label} tool input: ${details}`, { cause: error });
201
+ }
202
+ }
@@ -177,6 +177,262 @@ export type ShellActionNodeDefinition = WorkflowNodeCommon & {
177
177
 
178
178
  export type ActionNodeDefinition = FunctionActionNodeDefinition | ShellActionNodeDefinition;
179
179
 
180
+ export type HumanDecisionTextInput = {
181
+ kind: "text";
182
+ name: string;
183
+ prompt: string;
184
+ minLength: number;
185
+ maxLength: number;
186
+ };
187
+
188
+ export type HumanDecisionChoice = {
189
+ label: string;
190
+ input?: HumanDecisionTextInput;
191
+ };
192
+
193
+ export type HumanDecisionChoiceMap = Record<string, HumanDecisionChoice>;
194
+
195
+ export type DecisionPresentationParagraph = {
196
+ kind: "paragraph";
197
+ text: string;
198
+ };
199
+
200
+ export type DecisionPresentationSection = {
201
+ kind: "section";
202
+ title: string;
203
+ };
204
+
205
+ export type DecisionPresentationBullets = {
206
+ kind: "bullets";
207
+ items: string[];
208
+ };
209
+
210
+ export type DecisionPresentationFields = {
211
+ kind: "fields";
212
+ items: Array<{ label: string; value: string }>;
213
+ };
214
+
215
+ export type DecisionPresentationPreformatted = {
216
+ kind: "preformatted";
217
+ text: string;
218
+ };
219
+
220
+ export type DecisionPresentationBlock =
221
+ | DecisionPresentationParagraph
222
+ | DecisionPresentationSection
223
+ | DecisionPresentationBullets
224
+ | DecisionPresentationFields
225
+ | DecisionPresentationPreformatted;
226
+
227
+ export type DecisionPresentation = {
228
+ schema: "pi-workflows.decision-presentation.v1";
229
+ summary: string;
230
+ blocks: DecisionPresentationBlock[];
231
+ };
232
+
233
+ export type LegacyHumanDecisionPrompt = {
234
+ title: string;
235
+ body: unknown;
236
+ subject?: never;
237
+ presentation?: never;
238
+ revision?: never;
239
+ /** Optional absolute expiry. An expired request cannot accept an answer. */
240
+ expiresAt?: string;
241
+ };
242
+
243
+ export type PresentedHumanDecisionPrompt<TSubject = unknown> = {
244
+ title: string;
245
+ subject: TSubject;
246
+ presentation: DecisionPresentation;
247
+ body?: never;
248
+ /** Positive revision of the decision subject and presentation. Defaults to 1. */
249
+ revision?: number;
250
+ /** Optional absolute expiry. An expired request cannot accept an answer. */
251
+ expiresAt?: string;
252
+ };
253
+
254
+ export type HumanDecisionPrompt = LegacyHumanDecisionPrompt | PresentedHumanDecisionPrompt;
255
+
256
+ export type HumanDecisionAudience =
257
+ | string
258
+ | ((context: WorkflowNodeContext) => MaybePromise<string>);
259
+
260
+ export type HumanDecisionNodeContract = {
261
+ audience: HumanDecisionAudience;
262
+ choices: HumanDecisionChoiceMap;
263
+ request: (context: WorkflowNodeContext) => MaybePromise<HumanDecisionPrompt>;
264
+ };
265
+
266
+ type HumanDecisionRequestCommon = {
267
+ decisionId: string;
268
+ requestDigest: string;
269
+ runId: string;
270
+ workflowName: string;
271
+ nodeId: string;
272
+ attemptId: string;
273
+ audience: string;
274
+ title: string;
275
+ choices: HumanDecisionChoiceMap;
276
+ createdAt: string;
277
+ expiresAt?: string;
278
+ };
279
+
280
+ export type HumanDecisionRequestV1 = HumanDecisionRequestCommon & {
281
+ schema: "pi-workflows.human-decision-request.v1";
282
+ body: unknown;
283
+ };
284
+
285
+ export type HumanDecisionRequestV2 = HumanDecisionRequestCommon & {
286
+ schema: "pi-workflows.human-decision-request.v2";
287
+ subject: unknown;
288
+ presentation: DecisionPresentation;
289
+ revision: number;
290
+ subjectDigest: string;
291
+ presentationDigest: string;
292
+ };
293
+
294
+ export type HumanDecisionRequest = HumanDecisionRequestV1 | HumanDecisionRequestV2;
295
+
296
+ /**
297
+ * Complete operator-facing request passed to a decision channel. It excludes
298
+ * the canonical subject and legacy body by design.
299
+ */
300
+ export type HumanDecisionChannelRequest = HumanDecisionRequestCommon & {
301
+ schema: "pi-workflows.human-decision-channel-request.v1";
302
+ sourceSchema: HumanDecisionRequest["schema"];
303
+ presentation: DecisionPresentation;
304
+ presentationDigest: string;
305
+ revision: number;
306
+ };
307
+
308
+ export type HumanDecisionResponse = {
309
+ choice: string;
310
+ input?: Record<string, string>;
311
+ };
312
+
313
+ export type HumanDecisionAnswerSource = {
314
+ channel: string;
315
+ actorId: string;
316
+ eventId: string;
317
+ };
318
+
319
+ export type HumanDecisionSubmission = HumanDecisionResponse & {
320
+ decisionId: string;
321
+ requestDigest: string;
322
+ source: HumanDecisionAnswerSource;
323
+ idempotencyKey: string;
324
+ };
325
+
326
+ type AcceptedHumanDecisionCommon = {
327
+ decisionId: string;
328
+ requestDigest: string;
329
+ response: HumanDecisionResponse;
330
+ source: HumanDecisionAnswerSource;
331
+ idempotencyKey: string;
332
+ acceptedAt: string;
333
+ answerDigest: string;
334
+ };
335
+
336
+ export type AcceptedHumanDecisionV1 = AcceptedHumanDecisionCommon & {
337
+ schema: "pi-workflows.human-decision-accepted.v1";
338
+ };
339
+
340
+ export type AcceptedHumanDecisionV2 = AcceptedHumanDecisionCommon & {
341
+ schema: "pi-workflows.human-decision-accepted.v2";
342
+ subjectDigest: string;
343
+ presentationDigest: string;
344
+ revision: number;
345
+ };
346
+
347
+ export type AcceptedHumanDecision = AcceptedHumanDecisionV1 | AcceptedHumanDecisionV2;
348
+
349
+ type HumanDecisionReceiptCommon = {
350
+ decisionId: string;
351
+ requestDigest: string;
352
+ nodeId: string;
353
+ response: HumanDecisionResponse;
354
+ acceptedAt: string;
355
+ answerDigest: string;
356
+ };
357
+
358
+ export type HumanDecisionReceiptV1 = HumanDecisionReceiptCommon & {
359
+ schema: "pi-workflows.human-decision-receipt.v1";
360
+ };
361
+
362
+ export type HumanDecisionReceiptV2 = HumanDecisionReceiptCommon & {
363
+ schema: "pi-workflows.human-decision-receipt.v2";
364
+ subjectDigest: string;
365
+ presentationDigest: string;
366
+ revision: number;
367
+ };
368
+
369
+ export type HumanDecisionReceipt = HumanDecisionReceiptV1 | HumanDecisionReceiptV2;
370
+
371
+ export type HumanDecisionDeliveryRecordV1 = {
372
+ schema: "pi-workflows.human-decision-delivery.v1";
373
+ attemptId: string;
374
+ decisionId: string;
375
+ requestDigest: string;
376
+ channel: string;
377
+ state: "intent" | "confirmed" | "failed" | "unknown";
378
+ createdAt: string;
379
+ finishedAt?: string;
380
+ messageCount?: number;
381
+ errorCode?: string;
382
+ };
383
+
384
+ export type HumanDecisionDeliveryRecordV2 = {
385
+ schema: "pi-workflows.human-decision-delivery.v2";
386
+ attemptId: string;
387
+ decisionId: string;
388
+ requestDigest: string;
389
+ presentationDigest: string;
390
+ channel: string;
391
+ phase: "intent" | "part" | "complete";
392
+ state: "intent" | "confirmed" | "failed" | "unknown";
393
+ createdAt: string;
394
+ finishedAt?: string;
395
+ recipientIndex?: number;
396
+ partIndex?: number;
397
+ partCount?: number;
398
+ contentDigest?: string;
399
+ messageCount?: number;
400
+ errorCode?: string;
401
+ };
402
+
403
+ export type HumanDecisionDeliveryRecord =
404
+ | HumanDecisionDeliveryRecordV1
405
+ | HumanDecisionDeliveryRecordV2;
406
+
407
+ export type HumanDecisionSettlementRecord = {
408
+ schema: "pi-workflows.human-decision-settlement.v1";
409
+ attemptId: string;
410
+ decisionId: string;
411
+ requestDigest: string;
412
+ channel: string;
413
+ state: "confirmed" | "failed";
414
+ createdAt: string;
415
+ finishedAt: string;
416
+ errorCode?: string;
417
+ };
418
+
419
+ export type HumanDecisionCancellationRecord = {
420
+ schema: "pi-workflows.human-decision-cancellation.v1";
421
+ decisionId: string;
422
+ requestDigest: string;
423
+ cancelledAt: string;
424
+ reason: "cancelled" | "expired";
425
+ };
426
+
427
+ export type HumanDecisionContinuationRecord = {
428
+ schema: "pi-workflows.human-decision-continuation.v1";
429
+ decisionId: string;
430
+ requestDigest: string;
431
+ parentRunId: string;
432
+ runId: string;
433
+ createdAt: string;
434
+ };
435
+
180
436
  /**
181
437
  * A pause point. The run terminates with status `waiting` so a human (or an
182
438
  * external trigger) can decide how to continue. The optional `run` callback
@@ -186,6 +442,8 @@ export type CheckpointNodeDefinition = WorkflowNodeCommon & {
186
442
  nodeType: "checkpoint";
187
443
  summary?: string;
188
444
  run?: (context: WorkflowNodeContext) => MaybePromise<unknown>;
445
+ /** Typed verified-human request. Still executes as a checkpoint node. */
446
+ humanDecision?: HumanDecisionNodeContract;
189
447
  };
190
448
 
191
449
  export type WorkflowNodeDefinition =
@@ -204,12 +462,88 @@ export type WorkflowPresentationContext = {
204
462
  signal: AbortSignal;
205
463
  };
206
464
 
207
- export type WorkflowDefinition = {
465
+ /** Runtime parser that also carries its normalized TypeScript result type. */
466
+ export type WorkflowValueParser<T> = (value: unknown) => MaybePromise<T>;
467
+
468
+ export type WorkflowExitDefinition<TOutput = unknown> = {
469
+ /** Successful terminal node whose output leaves through this exit. */
470
+ from: string;
471
+ /** Optional runtime output normalizer and validator. */
472
+ validate?: WorkflowValueParser<TOutput>;
473
+ };
474
+
475
+ export type WorkflowExitMap = Record<string, WorkflowExitDefinition>;
476
+
477
+ export type WorkflowInputOf<TWorkflow> =
478
+ TWorkflow extends WorkflowDefinition<infer TInput, any, any> ? TInput : unknown;
479
+
480
+ export type WorkflowExitOutputs<TWorkflow> =
481
+ TWorkflow extends WorkflowDefinition<any, infer TExits, any>
482
+ ? {
483
+ [K in keyof TExits]: TExits[K] extends WorkflowExitDefinition<infer TOutput>
484
+ ? TOutput
485
+ : unknown;
486
+ }
487
+ : Record<string, unknown>;
488
+
489
+ export type WorkflowIncludedResult<TWorkflow> = {
490
+ [K in keyof WorkflowExitOutputs<TWorkflow>]: {
491
+ exit: K;
492
+ output: WorkflowExitOutputs<TWorkflow>[K];
493
+ };
494
+ }[keyof WorkflowExitOutputs<TWorkflow>];
495
+
496
+ export type WorkflowIncludeDefinition<
497
+ TWorkflow extends WorkflowDefinition<any, any, any> = WorkflowDefinition<any, any, any>,
498
+ > = {
499
+ /** Imported child definition or dynamic discovered name/path. */
500
+ workflow: TWorkflow | string;
501
+ /** Pure parent-to-child input mapping, evaluated on every mount entry. */
502
+ input?: (context: WorkflowNodeContext) => MaybePromise<WorkflowInputOf<TWorkflow>>;
503
+ /** Optional direct definition that supplies the contract for a dynamic reference. */
504
+ contract?: TWorkflow;
505
+ };
506
+
507
+ export type WorkflowIncludeMap = Record<string, WorkflowIncludeDefinition>;
508
+
509
+ export type WorkflowIncludeExitReference<TIncludes extends WorkflowIncludeMap> = {
510
+ [K in keyof TIncludes & string]: TIncludes[K] extends WorkflowIncludeDefinition<infer TWorkflow>
511
+ ? `${K}.${Extract<keyof WorkflowExitOutputs<TWorkflow>, string>}`
512
+ : never;
513
+ }[keyof TIncludes & string];
514
+
515
+ export type WorkflowTypedEdge<
516
+ TNodes extends Record<string, WorkflowNodeDefinition>,
517
+ TIncludes extends WorkflowIncludeMap,
518
+ > =
519
+ | {
520
+ from: (keyof TNodes & string) | WorkflowIncludeExitReference<TIncludes>;
521
+ to: (keyof TNodes & string) | (keyof TIncludes & string);
522
+ }
523
+ | {
524
+ from: keyof TNodes & string;
525
+ switch: {
526
+ on: string;
527
+ cases: Record<string, (keyof TNodes & string) | (keyof TIncludes & string)>;
528
+ };
529
+ };
530
+
531
+ export type WorkflowDefinition<
532
+ TInput = any,
533
+ TExits extends WorkflowExitMap = WorkflowExitMap,
534
+ TIncludes extends WorkflowIncludeMap = WorkflowIncludeMap,
535
+ > = {
208
536
  name: string;
537
+ /** Module URL used to attest directly imported child workflow files. */
538
+ source?: string;
539
+ /** Stable public input-and-exit contract identity for compatible overrides. */
540
+ contractId?: string;
541
+ /** Optional runtime input normalizer and validator. */
542
+ input?: WorkflowValueParser<TInput>;
209
543
  /** Optional human-readable run title (static or derived from input). */
210
544
  title?:
211
545
  | string
212
- | ((context: { input: unknown; workflowName: string }) => MaybePromise<string | undefined>);
546
+ | ((context: { input: TInput; workflowName: string }) => MaybePromise<string | undefined>);
213
547
  /**
214
548
  * Optional instructions for a normal assistant response after the run ends.
215
549
  * The Pi extension resolves this only after the final state is persisted;
@@ -220,6 +554,8 @@ export type WorkflowDefinition = {
220
554
  | ((context: WorkflowPresentationContext) => MaybePromise<string | undefined>);
221
555
  startAt: string;
222
556
  nodes: Record<string, WorkflowNodeDefinition>;
557
+ includes?: TIncludes;
558
+ exits?: TExits;
223
559
  edges: WorkflowEdge[];
224
560
  /** Guard against unbounded loops. Defaults to the engine's maxSteps. */
225
561
  maxSteps?: number;
@@ -308,6 +644,24 @@ export type WorkflowSource =
308
644
  | { kind: "builtin"; id: string; revision: string }
309
645
  | { kind: "file"; path: string; hash: string };
310
646
 
647
+ export type WorkflowMountedSource = {
648
+ mountPath: string[];
649
+ workflowName: string;
650
+ source: WorkflowSource;
651
+ };
652
+
653
+ export type WorkflowMountSnapshot = {
654
+ mountPath: string[];
655
+ workflowName: string;
656
+ entryNode: string;
657
+ exits: Record<string, string>;
658
+ maxSteps?: number;
659
+ };
660
+
661
+ export type WorkflowCompositionSnapshot = {
662
+ mounts: WorkflowMountSnapshot[];
663
+ };
664
+
311
665
  export type WorkflowRunState = {
312
666
  schema: "pi-workflows.run-state.v1";
313
667
  /**
@@ -329,6 +683,10 @@ export type WorkflowRunState = {
329
683
  runTitle?: string;
330
684
  /** Stable built-in identity or immutable file source used by this run. */
331
685
  workflowSource?: WorkflowSource;
686
+ /** Sorted immutable sources used by included workflow mounts. */
687
+ workflowSources?: WorkflowMountedSource[];
688
+ /** SHA-256 of the fully resolved definition snapshot. */
689
+ definitionDigest?: string;
332
690
  /** Legacy fields accepted only by the bounded built-in migration. */
333
691
  workflowPath?: string;
334
692
  workflowHash?: string;
@@ -346,6 +704,8 @@ export type WorkflowRunState = {
346
704
  currentAttemptId?: string;
347
705
  currentNodeStartedAt?: string;
348
706
  statusDetail?: string;
707
+ /** Redacted verified-human receipt carried by a continuation run. */
708
+ humanDecision?: HumanDecisionReceipt;
349
709
  /** True while the run is held at a step boundary by a pause request. */
350
710
  paused?: boolean;
351
711
  waitingOn?: string;
@@ -360,14 +720,24 @@ export type WorkflowNodeSnapshot = {
360
720
  summary?: string;
361
721
  expectedOutput?: string;
362
722
  actionExecution?: "function" | "shell";
723
+ mountPath?: string[];
724
+ localNodeId?: string;
725
+ includeTransition?: "entry" | "exit";
726
+ humanDecision?: {
727
+ audience: string;
728
+ dynamicAudience?: boolean;
729
+ choices: HumanDecisionChoiceMap;
730
+ };
363
731
  };
364
732
 
365
733
  export type WorkflowDefinitionSnapshot = {
366
734
  schema: "pi-workflows.definition-snapshot.v1";
367
735
  name: string;
736
+ contractId?: string;
368
737
  startAt: string;
369
738
  nodes: Record<string, WorkflowNodeSnapshot>;
370
739
  edges: WorkflowEdge[];
740
+ composition?: WorkflowCompositionSnapshot;
371
741
  };
372
742
 
373
743
  export type WorkflowTraceEvent = {
@@ -460,6 +830,8 @@ export type WorkflowRunManifest = {
460
830
  workflowName: string;
461
831
  runTitle?: string;
462
832
  workflowSource?: WorkflowSource;
833
+ workflowSources?: WorkflowMountedSource[];
834
+ definitionDigest?: string;
463
835
  startedAt: string;
464
836
  finishedAt?: string;
465
837
  status: WorkflowRunStatus;