@wichayutdew/pi-workflows 0.1.1

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 (45) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +752 -0
  3. package/agents/step.md +17 -0
  4. package/dist/index.js +4576 -0
  5. package/examples/mr-comments.workflow.yaml +115 -0
  6. package/examples/prompts/mr-comments/implement.md +8 -0
  7. package/examples/prompts/mr-comments/inspect.md +5 -0
  8. package/examples/prompts/mr-comments/plan.md +13 -0
  9. package/examples/prompts/mr-comments/verify.md +7 -0
  10. package/examples/settings.yaml +19 -0
  11. package/package.json +81 -0
  12. package/schemas/settings.schema.json +22 -0
  13. package/schemas/workflow.schema.json +585 -0
  14. package/src/command-names.ts +46 -0
  15. package/src/commands.ts +80 -0
  16. package/src/config/ceiling.ts +153 -0
  17. package/src/config/command-conflicts.ts +31 -0
  18. package/src/config/load.ts +327 -0
  19. package/src/config/types.ts +187 -0
  20. package/src/config/validate.ts +1145 -0
  21. package/src/digest.ts +23 -0
  22. package/src/engine/checkpoint.ts +30 -0
  23. package/src/engine/resume.ts +44 -0
  24. package/src/engine/state.ts +186 -0
  25. package/src/engine/transitions.ts +426 -0
  26. package/src/harness.ts +1676 -0
  27. package/src/index.ts +15 -0
  28. package/src/integrations/plannotator.ts +235 -0
  29. package/src/integrations/prompt-gate.ts +54 -0
  30. package/src/integrations/subagents/child-runtime.ts +306 -0
  31. package/src/integrations/subagents/client.ts +239 -0
  32. package/src/integrations/subagents/protocol.ts +304 -0
  33. package/src/policy/approved-commands.ts +225 -0
  34. package/src/policy/bash.ts +355 -0
  35. package/src/policy/completion-batch.ts +36 -0
  36. package/src/policy/immutable-input.ts +18 -0
  37. package/src/policy/tools.ts +150 -0
  38. package/src/preflight.ts +76 -0
  39. package/src/prompt.ts +146 -0
  40. package/src/runtime/completion-tool.ts +22 -0
  41. package/src/runtime/main-step-runtime.ts +227 -0
  42. package/src/runtime/serial-task-queue.ts +17 -0
  43. package/src/runtime/step-result.ts +85 -0
  44. package/src/workflow-list.ts +25 -0
  45. package/src/workflow-status.ts +611 -0
@@ -0,0 +1,585 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Pi workflow",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["version", "id", "command", "description", "start", "steps"],
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string"
10
+ },
11
+ "version": {
12
+ "const": 1
13
+ },
14
+ "id": {
15
+ "$ref": "#/$defs/identifier"
16
+ },
17
+ "command": {
18
+ "allOf": [
19
+ {
20
+ "$ref": "#/$defs/identifier"
21
+ },
22
+ {
23
+ "not": {
24
+ "enum": [
25
+ "arminsayshi",
26
+ "changelog",
27
+ "clone",
28
+ "compact",
29
+ "copy",
30
+ "debug",
31
+ "dementedelves",
32
+ "export",
33
+ "fork",
34
+ "hotkeys",
35
+ "import",
36
+ "login",
37
+ "logout",
38
+ "model",
39
+ "name",
40
+ "new",
41
+ "quit",
42
+ "reload",
43
+ "resume",
44
+ "scoped-models",
45
+ "session",
46
+ "settings",
47
+ "share",
48
+ "tree",
49
+ "trust",
50
+ "workflow-abort",
51
+ "workflow-list",
52
+ "workflow-pause",
53
+ "workflow-reload",
54
+ "workflow-resume",
55
+ "workflow-start",
56
+ "workflow-status"
57
+ ]
58
+ }
59
+ }
60
+ ]
61
+ },
62
+ "description": {
63
+ "type": "string",
64
+ "minLength": 1
65
+ },
66
+ "start": {
67
+ "$ref": "#/$defs/identifier"
68
+ },
69
+ "maxStepVisits": {
70
+ "type": "integer",
71
+ "minimum": 1,
72
+ "maximum": 100,
73
+ "default": 5
74
+ },
75
+ "summaryMaxChars": {
76
+ "type": "integer",
77
+ "minimum": 100,
78
+ "maximum": 50000,
79
+ "default": 4000
80
+ },
81
+ "steps": {
82
+ "type": "object",
83
+ "minProperties": 1,
84
+ "propertyNames": {
85
+ "$ref": "#/$defs/identifier"
86
+ },
87
+ "additionalProperties": {
88
+ "$ref": "#/$defs/step"
89
+ }
90
+ }
91
+ },
92
+ "$defs": {
93
+ "identifier": {
94
+ "type": "string",
95
+ "pattern": "^[a-z][a-z0-9-]{0,63}$"
96
+ },
97
+ "resourceName": {
98
+ "type": "string",
99
+ "pattern": "^[A-Za-z0-9_@./:+-]+$"
100
+ },
101
+ "subagentRuntimeName": {
102
+ "type": "string",
103
+ "pattern": "^[a-z0-9][a-z0-9-]*(?:\\.[a-z0-9][a-z0-9-]*)*$"
104
+ },
105
+ "prompt": {
106
+ "oneOf": [
107
+ {
108
+ "type": "string",
109
+ "minLength": 1
110
+ },
111
+ {
112
+ "type": "object",
113
+ "additionalProperties": false,
114
+ "required": ["file"],
115
+ "properties": {
116
+ "file": {
117
+ "type": "string",
118
+ "minLength": 1
119
+ }
120
+ }
121
+ }
122
+ ]
123
+ },
124
+ "bashRule": {
125
+ "type": "object",
126
+ "additionalProperties": false,
127
+ "required": ["executable"],
128
+ "allOf": [
129
+ {
130
+ "not": {
131
+ "required": ["argsPrefix", "argsPrefixes"]
132
+ }
133
+ }
134
+ ],
135
+ "properties": {
136
+ "executable": {
137
+ "type": "string",
138
+ "pattern": "^[A-Za-z0-9_./+-]+$"
139
+ },
140
+ "argsPrefix": {
141
+ "type": "array",
142
+ "items": {
143
+ "type": "string",
144
+ "pattern": "^\\S+$"
145
+ },
146
+ "default": []
147
+ },
148
+ "argsPrefixes": {
149
+ "type": "array",
150
+ "minItems": 1,
151
+ "uniqueItems": true,
152
+ "items": {
153
+ "type": "array",
154
+ "minItems": 1,
155
+ "items": {
156
+ "type": "string",
157
+ "pattern": "^\\S+$"
158
+ }
159
+ }
160
+ }
161
+ }
162
+ },
163
+ "bash": {
164
+ "type": "object",
165
+ "additionalProperties": false,
166
+ "required": ["mode"],
167
+ "allOf": [
168
+ {
169
+ "if": {
170
+ "properties": {
171
+ "mode": {
172
+ "const": "allow-list"
173
+ }
174
+ },
175
+ "required": ["mode"]
176
+ },
177
+ "then": {
178
+ "anyOf": [
179
+ {
180
+ "required": ["allow"],
181
+ "properties": {
182
+ "allow": {
183
+ "minItems": 1
184
+ }
185
+ }
186
+ },
187
+ {
188
+ "required": ["approvedSources"],
189
+ "properties": {
190
+ "approvedSources": {
191
+ "minItems": 1
192
+ }
193
+ }
194
+ }
195
+ ]
196
+ },
197
+ "else": {
198
+ "properties": {
199
+ "allow": {
200
+ "maxItems": 0
201
+ },
202
+ "approvedSources": {
203
+ "maxItems": 0
204
+ }
205
+ }
206
+ }
207
+ }
208
+ ],
209
+ "properties": {
210
+ "mode": {
211
+ "enum": ["deny", "read-only", "allow-list", "unrestricted"]
212
+ },
213
+ "allow": {
214
+ "type": "array",
215
+ "items": {
216
+ "$ref": "#/$defs/bashRule"
217
+ }
218
+ },
219
+ "approvedSources": {
220
+ "type": "array",
221
+ "uniqueItems": true,
222
+ "items": {
223
+ "enum": [
224
+ "verification-worker",
225
+ "verification-reviewer",
226
+ "remote-actions"
227
+ ]
228
+ }
229
+ }
230
+ }
231
+ },
232
+ "permissions": {
233
+ "type": "object",
234
+ "additionalProperties": false,
235
+ "properties": {
236
+ "tools": {
237
+ "type": "array",
238
+ "uniqueItems": true,
239
+ "items": {
240
+ "type": "string",
241
+ "pattern": "^[A-Za-z0-9_.:-]+$"
242
+ }
243
+ },
244
+ "mcp": {
245
+ "type": "array",
246
+ "uniqueItems": true,
247
+ "items": {
248
+ "type": "string",
249
+ "pattern": "^[A-Za-z0-9_.:-]+(?:/[A-Za-z0-9_.:-]+)?$"
250
+ }
251
+ },
252
+ "extensions": {
253
+ "type": "array",
254
+ "uniqueItems": true,
255
+ "items": {
256
+ "$ref": "#/$defs/resourceName"
257
+ }
258
+ },
259
+ "skills": {
260
+ "type": "array",
261
+ "uniqueItems": true,
262
+ "items": {
263
+ "$ref": "#/$defs/resourceName"
264
+ }
265
+ },
266
+ "bash": {
267
+ "$ref": "#/$defs/bash"
268
+ }
269
+ }
270
+ },
271
+ "requires": {
272
+ "type": "object",
273
+ "additionalProperties": false,
274
+ "properties": {
275
+ "tools": {
276
+ "type": "array",
277
+ "uniqueItems": true,
278
+ "items": {
279
+ "type": "string",
280
+ "pattern": "^[A-Za-z0-9_.:-]+$"
281
+ }
282
+ },
283
+ "extensions": {
284
+ "type": "array",
285
+ "uniqueItems": true,
286
+ "items": {
287
+ "$ref": "#/$defs/resourceName"
288
+ }
289
+ },
290
+ "skills": {
291
+ "type": "array",
292
+ "uniqueItems": true,
293
+ "items": {
294
+ "$ref": "#/$defs/resourceName"
295
+ }
296
+ }
297
+ }
298
+ },
299
+ "subagentTurnBudget": {
300
+ "type": "object",
301
+ "additionalProperties": false,
302
+ "required": ["maxTurns"],
303
+ "properties": {
304
+ "maxTurns": {
305
+ "type": "integer",
306
+ "minimum": 1,
307
+ "maximum": 1000
308
+ },
309
+ "graceTurns": {
310
+ "type": "integer",
311
+ "minimum": 0,
312
+ "maximum": 100
313
+ }
314
+ }
315
+ },
316
+ "subagentToolBudget": {
317
+ "type": "object",
318
+ "additionalProperties": false,
319
+ "required": ["hard"],
320
+ "properties": {
321
+ "soft": {
322
+ "type": "integer",
323
+ "minimum": 1,
324
+ "maximum": 100000
325
+ },
326
+ "hard": {
327
+ "type": "integer",
328
+ "minimum": 1,
329
+ "maximum": 100000
330
+ },
331
+ "block": {
332
+ "oneOf": [
333
+ {
334
+ "const": "*"
335
+ },
336
+ {
337
+ "type": "array",
338
+ "minItems": 1,
339
+ "uniqueItems": true,
340
+ "items": {
341
+ "type": "string",
342
+ "pattern": "^[A-Za-z0-9_.:-]+$"
343
+ }
344
+ }
345
+ ]
346
+ }
347
+ }
348
+ },
349
+ "subagent": {
350
+ "oneOf": [
351
+ {
352
+ "$ref": "#/$defs/subagentRuntimeName"
353
+ },
354
+ {
355
+ "type": "object",
356
+ "additionalProperties": false,
357
+ "properties": {
358
+ "agent": {
359
+ "$ref": "#/$defs/subagentRuntimeName",
360
+ "default": "pi-workflows.step"
361
+ },
362
+ "context": {
363
+ "enum": ["fresh", "fork"],
364
+ "default": "fresh"
365
+ },
366
+ "model": {
367
+ "$ref": "#/$defs/resourceName"
368
+ },
369
+ "timeoutMs": {
370
+ "type": "integer",
371
+ "minimum": 1000,
372
+ "maximum": 86400000,
373
+ "default": 900000
374
+ },
375
+ "turnBudget": {
376
+ "$ref": "#/$defs/subagentTurnBudget"
377
+ },
378
+ "toolBudget": {
379
+ "$ref": "#/$defs/subagentToolBudget"
380
+ },
381
+ "artifacts": {
382
+ "type": "boolean",
383
+ "default": false
384
+ }
385
+ }
386
+ }
387
+ ]
388
+ },
389
+ "subagentCeiling": {
390
+ "type": "object",
391
+ "additionalProperties": false,
392
+ "required": [
393
+ "agents",
394
+ "contexts",
395
+ "models",
396
+ "maxTimeoutMs",
397
+ "maxTurns",
398
+ "maxGraceTurns",
399
+ "maxToolCalls",
400
+ "artifacts"
401
+ ],
402
+ "properties": {
403
+ "agents": {
404
+ "type": "array",
405
+ "minItems": 1,
406
+ "uniqueItems": true,
407
+ "items": {
408
+ "$ref": "#/$defs/subagentRuntimeName"
409
+ }
410
+ },
411
+ "contexts": {
412
+ "type": "array",
413
+ "minItems": 1,
414
+ "uniqueItems": true,
415
+ "items": {
416
+ "enum": ["fresh", "fork"]
417
+ }
418
+ },
419
+ "models": {
420
+ "type": "array",
421
+ "uniqueItems": true,
422
+ "items": {
423
+ "$ref": "#/$defs/resourceName"
424
+ }
425
+ },
426
+ "maxTimeoutMs": {
427
+ "type": "integer",
428
+ "minimum": 1000,
429
+ "maximum": 86400000
430
+ },
431
+ "maxTurns": {
432
+ "type": "integer",
433
+ "minimum": 1,
434
+ "maximum": 1000
435
+ },
436
+ "maxGraceTurns": {
437
+ "type": "integer",
438
+ "minimum": 0,
439
+ "maximum": 100
440
+ },
441
+ "maxToolCalls": {
442
+ "type": "integer",
443
+ "minimum": 1,
444
+ "maximum": 100000
445
+ },
446
+ "artifacts": {
447
+ "type": "boolean"
448
+ }
449
+ }
450
+ },
451
+ "permissionCeiling": {
452
+ "type": "object",
453
+ "additionalProperties": false,
454
+ "properties": {
455
+ "tools": {
456
+ "type": "array",
457
+ "uniqueItems": true,
458
+ "items": {
459
+ "type": "string",
460
+ "pattern": "^[A-Za-z0-9_.:-]+$"
461
+ }
462
+ },
463
+ "mcp": {
464
+ "type": "array",
465
+ "uniqueItems": true,
466
+ "items": {
467
+ "type": "string",
468
+ "pattern": "^[A-Za-z0-9_.:-]+(?:/[A-Za-z0-9_.:-]+)?$"
469
+ }
470
+ },
471
+ "extensions": {
472
+ "type": "array",
473
+ "uniqueItems": true,
474
+ "items": {
475
+ "$ref": "#/$defs/resourceName"
476
+ }
477
+ },
478
+ "skills": {
479
+ "type": "array",
480
+ "uniqueItems": true,
481
+ "items": {
482
+ "$ref": "#/$defs/resourceName"
483
+ }
484
+ },
485
+ "bash": {
486
+ "$ref": "#/$defs/bash"
487
+ },
488
+ "subagent": {
489
+ "$ref": "#/$defs/subagentCeiling"
490
+ }
491
+ }
492
+ },
493
+ "gate": {
494
+ "oneOf": [
495
+ {
496
+ "type": "object",
497
+ "additionalProperties": false,
498
+ "required": ["submitOutcome", "approvedOutcome", "rejectedOutcome"],
499
+ "properties": {
500
+ "provider": {
501
+ "const": "prompt",
502
+ "default": "prompt"
503
+ },
504
+ "submitOutcome": {
505
+ "$ref": "#/$defs/identifier"
506
+ },
507
+ "approvedOutcome": {
508
+ "$ref": "#/$defs/identifier"
509
+ },
510
+ "rejectedOutcome": {
511
+ "$ref": "#/$defs/identifier"
512
+ }
513
+ }
514
+ },
515
+ {
516
+ "type": "object",
517
+ "additionalProperties": false,
518
+ "required": [
519
+ "provider",
520
+ "submitOutcome",
521
+ "approvedOutcome",
522
+ "rejectedOutcome"
523
+ ],
524
+ "properties": {
525
+ "provider": {
526
+ "const": "plannotator"
527
+ },
528
+ "submitOutcome": {
529
+ "$ref": "#/$defs/identifier"
530
+ },
531
+ "approvedOutcome": {
532
+ "$ref": "#/$defs/identifier"
533
+ },
534
+ "rejectedOutcome": {
535
+ "$ref": "#/$defs/identifier"
536
+ },
537
+ "timeoutMs": {
538
+ "type": "integer",
539
+ "minimum": 1000,
540
+ "maximum": 30000,
541
+ "default": 5000
542
+ }
543
+ }
544
+ }
545
+ ]
546
+ },
547
+ "step": {
548
+ "type": "object",
549
+ "additionalProperties": false,
550
+ "required": ["prompt", "transitions"],
551
+ "properties": {
552
+ "title": {
553
+ "type": "string",
554
+ "minLength": 1
555
+ },
556
+ "prompt": {
557
+ "$ref": "#/$defs/prompt"
558
+ },
559
+ "subagent": {
560
+ "$ref": "#/$defs/subagent"
561
+ },
562
+ "permissions": {
563
+ "$ref": "#/$defs/permissions"
564
+ },
565
+ "requires": {
566
+ "$ref": "#/$defs/requires"
567
+ },
568
+ "transitions": {
569
+ "type": "object",
570
+ "minProperties": 1,
571
+ "propertyNames": {
572
+ "$ref": "#/$defs/identifier"
573
+ },
574
+ "additionalProperties": {
575
+ "type": "string",
576
+ "pattern": "^(?:\\$done|\\$pause|[a-z][a-z0-9-]{0,63})$"
577
+ }
578
+ },
579
+ "gate": {
580
+ "$ref": "#/$defs/gate"
581
+ }
582
+ }
583
+ }
584
+ }
585
+ }
@@ -0,0 +1,46 @@
1
+ export const HARNESS_COMMAND_NAMES = [
2
+ 'workflow-abort',
3
+ 'workflow-list',
4
+ 'workflow-pause',
5
+ 'workflow-reload',
6
+ 'workflow-resume',
7
+ 'workflow-start',
8
+ 'workflow-status',
9
+ ] as const;
10
+
11
+ /**
12
+ * Interactive commands handled by Pi before extension command dispatch.
13
+ * Hidden diagnostic commands are included because an alias cannot reach them.
14
+ */
15
+ export const PI_BUILTIN_COMMAND_NAMES = [
16
+ 'arminsayshi',
17
+ 'changelog',
18
+ 'clone',
19
+ 'compact',
20
+ 'copy',
21
+ 'debug',
22
+ 'dementedelves',
23
+ 'export',
24
+ 'fork',
25
+ 'hotkeys',
26
+ 'import',
27
+ 'login',
28
+ 'logout',
29
+ 'model',
30
+ 'name',
31
+ 'new',
32
+ 'quit',
33
+ 'reload',
34
+ 'resume',
35
+ 'scoped-models',
36
+ 'session',
37
+ 'settings',
38
+ 'share',
39
+ 'tree',
40
+ 'trust',
41
+ ] as const;
42
+
43
+ export const RESERVED_COMMAND_NAMES = new Set<string>([
44
+ ...HARNESS_COMMAND_NAMES,
45
+ ...PI_BUILTIN_COMMAND_NAMES,
46
+ ]);
@@ -0,0 +1,80 @@
1
+ import type {
2
+ ExtensionAPI,
3
+ ExtensionCommandContext,
4
+ } from '@earendil-works/pi-coding-agent';
5
+
6
+ export interface WorkflowCommandController {
7
+ workflowIds(): string[];
8
+ list(ctx: ExtensionCommandContext): Promise<void>;
9
+ start(
10
+ workflowId: string,
11
+ input: string,
12
+ ctx: ExtensionCommandContext,
13
+ ): Promise<void>;
14
+ pause(reason: string, ctx: ExtensionCommandContext): Promise<void>;
15
+ resume(ctx: ExtensionCommandContext): Promise<void>;
16
+ abort(reason: string, ctx: ExtensionCommandContext): Promise<void>;
17
+ reload(ctx: ExtensionCommandContext): Promise<void>;
18
+ status(ctx: ExtensionCommandContext): Promise<void>;
19
+ }
20
+
21
+ function splitFirst(value: string): [string, string] {
22
+ const trimmed = value.trim();
23
+ const separator = trimmed.search(/\s/);
24
+ if (separator === -1) return [trimmed, ''];
25
+ return [trimmed.slice(0, separator), trimmed.slice(separator).trim()];
26
+ }
27
+
28
+ export function registerHarnessCommands(
29
+ pi: ExtensionAPI,
30
+ controller: WorkflowCommandController,
31
+ ): void {
32
+ pi.registerCommand('workflow-list', {
33
+ description: 'List loaded declarative workflows',
34
+ handler: async (_args, ctx) => controller.list(ctx),
35
+ });
36
+
37
+ pi.registerCommand('workflow-start', {
38
+ description: 'Start a workflow: /workflow-start <id> [input]',
39
+ getArgumentCompletions: (prefix) => {
40
+ const items = controller
41
+ .workflowIds()
42
+ .filter((id) => id.startsWith(prefix))
43
+ .map((id) => ({ value: id, label: id }));
44
+ return items.length > 0 ? items : null;
45
+ },
46
+ handler: async (args, ctx) => {
47
+ const [workflowId, input] = splitFirst(args);
48
+ if (!workflowId) {
49
+ ctx.ui.notify('Usage: /workflow-start <id> [input]', 'warning');
50
+ return;
51
+ }
52
+ await controller.start(workflowId, input, ctx);
53
+ },
54
+ });
55
+
56
+ pi.registerCommand('workflow-pause', {
57
+ description: 'Pause the active workflow without losing its checkpoint',
58
+ handler: async (reason, ctx) => controller.pause(reason.trim(), ctx),
59
+ });
60
+
61
+ pi.registerCommand('workflow-resume', {
62
+ description: 'Reload configuration and resume the paused workflow',
63
+ handler: async (_args, ctx) => controller.resume(ctx),
64
+ });
65
+
66
+ pi.registerCommand('workflow-abort', {
67
+ description: 'Abort the active workflow',
68
+ handler: async (reason, ctx) => controller.abort(reason.trim(), ctx),
69
+ });
70
+
71
+ pi.registerCommand('workflow-reload', {
72
+ description: 'Reload workflow files while no workflow is running',
73
+ handler: async (_args, ctx) => controller.reload(ctx),
74
+ });
75
+
76
+ pi.registerCommand('workflow-status', {
77
+ description: 'Open the active workflow status board',
78
+ handler: async (_args, ctx) => controller.status(ctx),
79
+ });
80
+ }