@osolmaz/pi-workflows 0.12.0 → 0.12.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.
- package/README.md +4 -3
- package/dist/builtins/autoimplement.workflow.d.ts +485 -126
- package/dist/builtins/autoimplement.workflow.js +17 -105
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/builtins/catalog.js +4 -4
- package/dist/builtins/index.d.ts +2 -1
- package/dist/builtins/index.js +2 -1
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/monitor.workflow.d.ts +2 -4
- package/dist/builtins/monitor.workflow.js +26 -128
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/builtins/pi-agent-group.d.ts +72 -0
- package/dist/builtins/pi-agent-group.js +1087 -0
- package/dist/builtins/pi-agent-group.js.map +1 -0
- package/dist/builtins/plan-approval.workflow.d.ts +39 -5
- package/dist/builtins/plan-approval.workflow.js +92 -14
- package/dist/builtins/plan-approval.workflow.js.map +1 -1
- package/dist/builtins/plan-change.workflow.d.ts +301 -0
- package/dist/builtins/plan-change.workflow.js +256 -0
- package/dist/builtins/plan-change.workflow.js.map +1 -0
- package/dist/builtins/plan-presentation.js +2 -2
- package/dist/builtins/plan-presentation.js.map +1 -1
- package/dist/builtins/sanity-check.workflow.d.ts +5 -3
- package/dist/builtins/sanity-check.workflow.js +105 -21
- package/dist/builtins/sanity-check.workflow.js.map +1 -1
- package/dist/extension/decision-channels.d.ts +2 -2
- package/dist/extension/decision-channels.js +22 -28
- package/dist/extension/decision-channels.js.map +1 -1
- package/dist/extension/index.js +62 -33
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/session-events.d.ts +2 -2
- package/dist/extension/widget.js +23 -3
- package/dist/extension/widget.js.map +1 -1
- package/dist/render/graph-render.js +1 -2
- package/dist/render/graph-render.js.map +1 -1
- package/dist/viewer/render.js +7 -6
- package/dist/viewer/render.js.map +1 -1
- package/dist/workflows/catalog.js +7 -2
- package/dist/workflows/catalog.js.map +1 -1
- package/dist/workflows/composition.js +8 -0
- package/dist/workflows/composition.js.map +1 -1
- package/dist/workflows/decision-presentation.d.ts +1 -1
- package/dist/workflows/decision-presentation.js +51 -38
- package/dist/workflows/decision-presentation.js.map +1 -1
- package/dist/workflows/engine.d.ts +2 -2
- package/dist/workflows/engine.js +14 -13
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/errors.d.ts +13 -0
- package/dist/workflows/errors.js +15 -0
- package/dist/workflows/errors.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +16 -4
- package/dist/workflows/human-decision.js +175 -72
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +1 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/progress.d.ts +1 -0
- package/dist/workflows/progress.js +15 -3
- package/dist/workflows/progress.js.map +1 -1
- package/dist/workflows/schema.js +10 -0
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.js +5 -0
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +33 -45
- package/docs/HUMAN_DECISIONS.md +25 -35
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +14 -24
- package/docs/MONITOR.md +5 -11
- package/docs/WORKFLOW_COMPOSITION.md +8 -7
- package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +322 -0
- package/docs/plans/2026-08-21-sanity-check-plan.md +202 -94
- package/docs/run-bundles.md +6 -6
- package/docs/workflows.md +26 -6
- package/examples/workflows/approved-plan.workflow.ts +19 -46
- package/herdr-plugin.toml +1 -1
- package/package.json +7 -7
- package/schemas/human-decision-accepted-v1.schema.json +15 -3
- package/schemas/human-decision-continuation-v1.schema.json +10 -1
- package/schemas/human-decision-delivery-v1.schema.json +8 -0
- package/schemas/human-decision-receipt-v1.schema.json +8 -0
- package/schemas/human-decision-request-v1.schema.json +24 -4
- package/skills/autoimplement/SKILL.md +27 -0
- package/skills/monitor/SKILL.md +31 -3
- package/skills/pi-workflows/SKILL.md +2 -1
- package/skills/sanity-check/SKILL.md +44 -0
- package/src/builtins/autoimplement.workflow.ts +19 -118
- package/src/builtins/catalog.ts +4 -4
- package/src/builtins/index.ts +11 -0
- package/src/builtins/monitor.workflow.ts +27 -150
- package/src/builtins/pi-agent-group.ts +1407 -0
- package/src/builtins/plan-approval.workflow.ts +157 -24
- package/src/builtins/plan-change.workflow.ts +321 -0
- package/src/builtins/plan-presentation.ts +2 -2
- package/src/builtins/sanity-check.workflow.ts +186 -41
- package/src/extension/decision-channels.ts +29 -59
- package/src/extension/index.ts +79 -41
- package/src/extension/session-events.ts +2 -2
- package/src/extension/widget.ts +24 -5
- package/src/render/graph-render.ts +1 -2
- package/src/viewer/render.ts +7 -6
- package/src/workflows/catalog.ts +7 -2
- package/src/workflows/composition.ts +9 -0
- package/src/workflows/decision-presentation.ts +56 -43
- package/src/workflows/engine.ts +17 -15
- package/src/workflows/errors.ts +24 -0
- package/src/workflows/human-decision.ts +218 -101
- package/src/workflows/index.ts +5 -11
- package/src/workflows/progress.ts +18 -3
- package/src/workflows/schema.ts +17 -0
- package/src/workflows/store.ts +5 -0
- package/src/workflows/types.ts +39 -56
- package/dist/builtins/sanity-check-session.d.ts +0 -17
- package/dist/builtins/sanity-check-session.js +0 -168
- package/dist/builtins/sanity-check-session.js.map +0 -1
- package/schemas/human-decision-accepted-v2.schema.json +0 -50
- package/schemas/human-decision-delivery-v2.schema.json +0 -36
- package/schemas/human-decision-receipt-v2.schema.json +0 -39
- package/schemas/human-decision-request-v2.schema.json +0 -69
- package/schemas/human-decision-resolution-v2.schema.json +0 -27
- package/src/builtins/sanity-check-session.ts +0 -205
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osolmaz/pi-workflows",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Workflow and controller runtime with a live terminal viewer for the pi coding agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -69,9 +69,9 @@
|
|
|
69
69
|
"jiti": "^2.7.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@earendil-works/pi-ai": "
|
|
73
|
-
"@earendil-works/pi-coding-agent": "
|
|
74
|
-
"@earendil-works/pi-tui": "
|
|
72
|
+
"@earendil-works/pi-ai": "0.84.2",
|
|
73
|
+
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
74
|
+
"@earendil-works/pi-tui": "0.84.2",
|
|
75
75
|
"@types/better-sqlite3": "^7.6.13",
|
|
76
76
|
"@types/node": "^26.1.1",
|
|
77
77
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
"vitest": "^4.1.10"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
|
-
"@earendil-works/pi-ai": "
|
|
87
|
-
"@earendil-works/pi-coding-agent": "
|
|
88
|
-
"@earendil-works/pi-tui": "
|
|
86
|
+
"@earendil-works/pi-ai": ">=0.84.2 <0.85.0",
|
|
87
|
+
"@earendil-works/pi-coding-agent": ">=0.84.2 <0.85.0",
|
|
88
|
+
"@earendil-works/pi-tui": ">=0.84.2 <0.85.0",
|
|
89
89
|
"typebox": "*"
|
|
90
90
|
},
|
|
91
91
|
"engines": {
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
"schema",
|
|
9
9
|
"decisionId",
|
|
10
10
|
"requestDigest",
|
|
11
|
+
"subjectDigest",
|
|
12
|
+
"presentationDigest",
|
|
13
|
+
"revision",
|
|
11
14
|
"response",
|
|
12
|
-
"
|
|
13
|
-
"idempotencyKey",
|
|
15
|
+
"provenance",
|
|
14
16
|
"acceptedAt",
|
|
15
17
|
"answerDigest"
|
|
16
18
|
],
|
|
@@ -18,6 +20,9 @@
|
|
|
18
20
|
"schema": { "const": "pi-workflows.human-decision-accepted.v1" },
|
|
19
21
|
"decisionId": { "type": "string" },
|
|
20
22
|
"requestDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
23
|
+
"subjectDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
24
|
+
"presentationDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
25
|
+
"revision": { "type": "integer", "minimum": 1 },
|
|
21
26
|
"response": {
|
|
22
27
|
"type": "object",
|
|
23
28
|
"additionalProperties": false,
|
|
@@ -27,6 +32,7 @@
|
|
|
27
32
|
"input": { "type": "object", "additionalProperties": { "type": "string" } }
|
|
28
33
|
}
|
|
29
34
|
},
|
|
35
|
+
"provenance": { "enum": ["human", "timeout"] },
|
|
30
36
|
"source": {
|
|
31
37
|
"type": "object",
|
|
32
38
|
"additionalProperties": false,
|
|
@@ -40,5 +46,11 @@
|
|
|
40
46
|
"idempotencyKey": { "type": "string" },
|
|
41
47
|
"acceptedAt": { "type": "string", "format": "date-time" },
|
|
42
48
|
"answerDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }
|
|
43
|
-
}
|
|
49
|
+
},
|
|
50
|
+
"allOf": [
|
|
51
|
+
{
|
|
52
|
+
"if": { "properties": { "provenance": { "const": "human" } } },
|
|
53
|
+
"then": { "required": ["source", "idempotencyKey"] }
|
|
54
|
+
}
|
|
55
|
+
]
|
|
44
56
|
}
|
|
@@ -4,11 +4,20 @@
|
|
|
4
4
|
"title": "pi-workflows human decision continuation v1",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
|
-
"required": [
|
|
7
|
+
"required": [
|
|
8
|
+
"schema",
|
|
9
|
+
"decisionId",
|
|
10
|
+
"requestDigest",
|
|
11
|
+
"provenance",
|
|
12
|
+
"parentRunId",
|
|
13
|
+
"runId",
|
|
14
|
+
"createdAt"
|
|
15
|
+
],
|
|
8
16
|
"properties": {
|
|
9
17
|
"schema": { "const": "pi-workflows.human-decision-continuation.v1" },
|
|
10
18
|
"decisionId": { "type": "string" },
|
|
11
19
|
"requestDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
20
|
+
"provenance": { "enum": ["human", "timeout"] },
|
|
12
21
|
"parentRunId": { "type": "string" },
|
|
13
22
|
"runId": { "type": "string" },
|
|
14
23
|
"createdAt": { "type": "string", "format": "date-time" }
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"attemptId",
|
|
10
10
|
"decisionId",
|
|
11
11
|
"requestDigest",
|
|
12
|
+
"presentationDigest",
|
|
12
13
|
"channel",
|
|
14
|
+
"phase",
|
|
13
15
|
"state",
|
|
14
16
|
"createdAt"
|
|
15
17
|
],
|
|
@@ -18,10 +20,16 @@
|
|
|
18
20
|
"attemptId": { "type": "string" },
|
|
19
21
|
"decisionId": { "type": "string" },
|
|
20
22
|
"requestDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
23
|
+
"presentationDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
21
24
|
"channel": { "type": "string" },
|
|
25
|
+
"phase": { "enum": ["intent", "part", "complete"] },
|
|
22
26
|
"state": { "enum": ["intent", "confirmed", "failed", "unknown"] },
|
|
23
27
|
"createdAt": { "type": "string", "format": "date-time" },
|
|
24
28
|
"finishedAt": { "type": "string", "format": "date-time" },
|
|
29
|
+
"recipientIndex": { "type": "integer", "minimum": 1 },
|
|
30
|
+
"partIndex": { "type": "integer", "minimum": 1 },
|
|
31
|
+
"partCount": { "type": "integer", "minimum": 1, "maximum": 20 },
|
|
32
|
+
"contentDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
25
33
|
"messageCount": { "type": "integer", "minimum": 0 },
|
|
26
34
|
"errorCode": { "type": "string" }
|
|
27
35
|
}
|
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
"schema",
|
|
9
9
|
"decisionId",
|
|
10
10
|
"requestDigest",
|
|
11
|
+
"subjectDigest",
|
|
12
|
+
"presentationDigest",
|
|
13
|
+
"revision",
|
|
11
14
|
"nodeId",
|
|
12
15
|
"response",
|
|
16
|
+
"provenance",
|
|
13
17
|
"acceptedAt",
|
|
14
18
|
"answerDigest"
|
|
15
19
|
],
|
|
@@ -17,6 +21,9 @@
|
|
|
17
21
|
"schema": { "const": "pi-workflows.human-decision-receipt.v1" },
|
|
18
22
|
"decisionId": { "type": "string" },
|
|
19
23
|
"requestDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
24
|
+
"subjectDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
25
|
+
"presentationDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
26
|
+
"revision": { "type": "integer", "minimum": 1 },
|
|
20
27
|
"nodeId": { "type": "string" },
|
|
21
28
|
"response": {
|
|
22
29
|
"type": "object",
|
|
@@ -27,6 +34,7 @@
|
|
|
27
34
|
"input": { "type": "object", "additionalProperties": { "type": "string" } }
|
|
28
35
|
}
|
|
29
36
|
},
|
|
37
|
+
"provenance": { "enum": ["human", "timeout"] },
|
|
30
38
|
"acceptedAt": { "type": "string", "format": "date-time" },
|
|
31
39
|
"answerDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }
|
|
32
40
|
}
|
|
@@ -8,13 +8,17 @@
|
|
|
8
8
|
"schema",
|
|
9
9
|
"decisionId",
|
|
10
10
|
"requestDigest",
|
|
11
|
+
"subjectDigest",
|
|
12
|
+
"presentationDigest",
|
|
11
13
|
"runId",
|
|
12
14
|
"workflowName",
|
|
13
15
|
"nodeId",
|
|
14
16
|
"attemptId",
|
|
15
17
|
"audience",
|
|
16
18
|
"title",
|
|
17
|
-
"
|
|
19
|
+
"subject",
|
|
20
|
+
"presentation",
|
|
21
|
+
"revision",
|
|
18
22
|
"choices",
|
|
19
23
|
"createdAt"
|
|
20
24
|
],
|
|
@@ -22,13 +26,19 @@
|
|
|
22
26
|
"schema": { "const": "pi-workflows.human-decision-request.v1" },
|
|
23
27
|
"decisionId": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$" },
|
|
24
28
|
"requestDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
29
|
+
"subjectDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
30
|
+
"presentationDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
25
31
|
"runId": { "type": "string" },
|
|
26
32
|
"workflowName": { "type": "string" },
|
|
27
33
|
"nodeId": { "type": "string" },
|
|
28
34
|
"attemptId": { "type": "string" },
|
|
29
35
|
"audience": { "type": "string" },
|
|
30
36
|
"title": { "type": "string", "minLength": 1 },
|
|
31
|
-
"
|
|
37
|
+
"subject": true,
|
|
38
|
+
"presentation": {
|
|
39
|
+
"$ref": "decision-presentation-v1.schema.json"
|
|
40
|
+
},
|
|
41
|
+
"revision": { "type": "integer", "minimum": 1 },
|
|
32
42
|
"choices": {
|
|
33
43
|
"type": "object",
|
|
34
44
|
"minProperties": 1,
|
|
@@ -54,6 +64,16 @@
|
|
|
54
64
|
}
|
|
55
65
|
},
|
|
56
66
|
"createdAt": { "type": "string", "format": "date-time" },
|
|
57
|
-
"expiresAt": { "type": "string", "format": "date-time" }
|
|
58
|
-
|
|
67
|
+
"expiresAt": { "type": "string", "format": "date-time" },
|
|
68
|
+
"defaultResponse": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"required": ["choice"],
|
|
72
|
+
"properties": {
|
|
73
|
+
"choice": { "type": "string" },
|
|
74
|
+
"input": { "type": "object", "additionalProperties": { "type": "string" } }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"dependentRequired": { "defaultResponse": ["expiresAt"] }
|
|
59
79
|
}
|
|
@@ -20,6 +20,7 @@ Build the input as follows:
|
|
|
20
20
|
- `baseBranch`: Use the requested base or the repository default branch.
|
|
21
21
|
- `merge`: Set `true` only when the user explicitly requested merge or an applicable standing instruction authorizes it. Otherwise set `false`.
|
|
22
22
|
- `documents`: Include known canonical plan or specification paths. Use an empty array when none are known.
|
|
23
|
+
- `approval`: Omit it for the default behavior: ask on each new plan and continue after 10 minutes without an answer. Use `{ "mode": "required" }` when the user says to block on plan changes. Use `{ "mode": "skip" }` when the user says to continue without asking about plan changes.
|
|
23
24
|
- `concurrency`: Include it only when the conversation gives explicit limits.
|
|
24
25
|
|
|
25
26
|
When one repository is clearly named, derive the scope without asking the user to restate it. A safe derived scope permits only work needed for the task in that repository, including local verification and normal branch and pull-request publication. It excludes unrelated repositories, merge, release, deployment, credentials, and policy changes unless those actions are explicitly authorized.
|
|
@@ -51,6 +52,32 @@ Replace the example values below with facts from the conversation, then make one
|
|
|
51
52
|
}
|
|
52
53
|
```
|
|
53
54
|
|
|
55
|
+
### Plan-change decisions
|
|
56
|
+
|
|
57
|
+
The workflow gates only plans that it creates or changes after the run starts. A supplied or discovered existing plan does not receive another decision.
|
|
58
|
+
|
|
59
|
+
Use required approval when the user says to block on plan changes:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"approval": {
|
|
64
|
+
"mode": "required"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Skip plan decisions when the user says to accept every new plan immediately:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"approval": {
|
|
74
|
+
"mode": "skip"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Omit `approval` for autonomous mode. It asks the `operator` audience and continues with the exact presented plan after 10 minutes without an accepted answer. The workflow owns this decision. The model must not answer the protected decision through the workflow tool.
|
|
80
|
+
|
|
54
81
|
Do not manually duplicate stages already owned by the workflow. Autoimplement runs independent pi-reviewer commands, pending CI watches, and local verification commands from separate repositories in bounded batches. It keeps model turns, fixes, pushes, comment changes, merges, and releases ordered. One repository uses the same batch path with concurrency one.
|
|
55
82
|
|
|
56
83
|
When this skill is loaded inside an active workflow step, do not start another workflow. Complete the current step contract with the available tools.
|
package/skills/monitor/SKILL.md
CHANGED
|
@@ -29,7 +29,7 @@ Derive the workflow input from the full conversation:
|
|
|
29
29
|
- `task`: State the complete objective, the exact current target and stable identifiers, authoritative status sources, durable progress and final-output surfaces, routine actions authorized by the monitor request, other recorded approvals, immutable boundaries, cost and credential rules, and required validation or downstream operations.
|
|
30
30
|
- `everyMinutes`: Use the user's interval when present. Use `30` when the user gives no interval. The built-in workflow accepts intervals from 1 minute through 24 hours.
|
|
31
31
|
- `stopWhen`: Infer verified completion from the full conversation. Describe completion of the complete objective, not only the end of one physical process. Also name material blockers that require human intervention.
|
|
32
|
-
- `repair`: Include this object only when the request or an existing approval authorizes mutation. Set `authorized: true` and record the repository, scope, base branch, merge policy, and constraints that apply. Omit it for observation-only work.
|
|
32
|
+
- `repair`: Include this object only when the request or an existing approval authorizes mutation. Set `authorized: true` and record the repository, scope, base branch, merge policy, and constraints that apply. Omit it for observation-only work. Omit `repair.approval` for the default behavior: ask on each new repair plan and continue after 10 minutes without an answer. Use `{ "mode": "required" }` to block on plan changes or `{ "mode": "skip" }` to continue without asking.
|
|
33
33
|
|
|
34
34
|
Replace the example values below with facts from the conversation, then make one start call:
|
|
35
35
|
|
|
@@ -65,9 +65,37 @@ When the conversation gives no clear finish criterion, set `stopWhen` to `Stop o
|
|
|
65
65
|
|
|
66
66
|
Do not invent a finite check count. Omit `maxChecks` unless the user explicitly requests one. The workflow host can apply its own safety upper bound. Disclose that bound if it appears.
|
|
67
67
|
|
|
68
|
-
When repair is authorized, route a concrete code or design defect through the monitor's
|
|
68
|
+
When repair is authorized, route a concrete code or design defect through the monitor's shared plan-change path. Supply the problem, observed evidence, and a stable fingerprint of the issue plus target state. The path runs Autoplan, Autodoc, the configured plan decision, and Autoimplement, then checks the target again. Autoimplement does not ask again for the plan selected by Monitor. It uses the same shared path if later evidence requires another plan. Do not copy their prompts into the monitor task.
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
### Repair plan decisions
|
|
71
|
+
|
|
72
|
+
Omit `repair.approval` for autonomous mode. It asks the `operator` audience and continues with the exact presented plan after 10 minutes without an accepted answer.
|
|
73
|
+
|
|
74
|
+
Block until the operator answers:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"repair": {
|
|
79
|
+
"approval": {
|
|
80
|
+
"mode": "required"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Continue without asking:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"repair": {
|
|
91
|
+
"approval": {
|
|
92
|
+
"mode": "skip"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`continue` starts implementation. `stop` ends the repair truthfully. `replan` preserves the exact operator text, sends it through the shared plan-change workflow, records the revised plan, and asks again. The model-facing workflow answer tool cannot answer this gate.
|
|
71
99
|
|
|
72
100
|
## Keep routine work moving
|
|
73
101
|
|
|
@@ -71,7 +71,8 @@ Follow these rules:
|
|
|
71
71
|
- Give included workflows named exits, map their input explicitly, and keep parent edges out of child internals.
|
|
72
72
|
- Keep `compute` pure. Put external effects in agent, function-action, or shell-action nodes.
|
|
73
73
|
- Use structured node outputs for routing.
|
|
74
|
-
- Use an ordinary checkpoint for external continuation data that the model may submit. Use `humanDecision()` for a verified human choice
|
|
74
|
+
- Use an ordinary checkpoint for external continuation data that the model may submit. Use `humanDecision()` for a verified human choice. Use its typed `onTimeout` policy only when the workflow may supply a named automatic response after a durable deadline. A timeout response is recorded as policy provenance, not as a human answer.
|
|
75
|
+
- Use the shared internal plan-change workflow for Autoplan, Autodoc, plan approval, and bounded exact-text replanning. Do not copy that sequence into Autoimplement, Monitor, or another workflow.
|
|
75
76
|
- Set explicit step and command timeouts.
|
|
76
77
|
- Bound ordinary loops with `maxSteps` or another clear finish rule.
|
|
77
78
|
- Use a controller instead of a workflow for indefinite resource reconciliation.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sanity-check
|
|
3
|
+
description: Use when the user asks for a read-only sanity check of a repository contribution, including whether it is necessary, duplicates existing code, adds justified contracts, or has proportionate scope and tests. Starts the built-in sanity-check workflow and returns an evidence-backed keep, simplify, refactor, drop, or needs-evidence verdict.
|
|
4
|
+
compatibility: Requires pi-workflows and the built-in sanity-check workflow.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Sanity Check
|
|
8
|
+
|
|
9
|
+
## Start the workflow
|
|
10
|
+
|
|
11
|
+
Use the built-in `sanity-check` workflow when it is available. At top level, list workflows, build the complete input, and start `sanity-check` once.
|
|
12
|
+
|
|
13
|
+
The workflow reviews the repository of the current Pi session. Before starting, confirm that Pi's current working directory is inside the repository that owns the contribution. Do not claim to check a different repository. If the current directory is wrong, start or use a Pi session in the correct repository first.
|
|
14
|
+
|
|
15
|
+
Build the input as follows:
|
|
16
|
+
|
|
17
|
+
- `mode`: Use `serial` unless the user explicitly requests parallel review. Serial mode reviews all four areas in one isolated session. Parallel mode runs one isolated review per area with concurrency four. Both modes run a separate verification review.
|
|
18
|
+
- `baseRef`: Use the requested base Git reference. When the base is clear, pass it explicitly, such as `origin/main`. Omit it only when the workflow should derive the base from `origin/HEAD`, the current branch upstream, or `HEAD^`.
|
|
19
|
+
|
|
20
|
+
Replace the example values below with facts from the conversation, then make one start call:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"action": "start",
|
|
25
|
+
"workflow": "sanity-check",
|
|
26
|
+
"input": {
|
|
27
|
+
"mode": "serial",
|
|
28
|
+
"baseRef": "origin/main"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The workflow is read-only. It collects committed and working-tree evidence, reads matching pull-request metadata when available, runs isolated reviews, verifies their claims, and reports one verdict. It does not edit files, post comments, or fix findings.
|
|
34
|
+
|
|
35
|
+
When this skill is loaded inside an active workflow step, do not start another workflow. Complete the current step contract.
|
|
36
|
+
|
|
37
|
+
Outside Pi, or when the workflow is unavailable:
|
|
38
|
+
|
|
39
|
+
1. Collect the committed diff from the selected base through `HEAD`, the working-tree diff, untracked files, and matching pull-request context.
|
|
40
|
+
2. Review necessity, duplication, contracts, and scope and tests.
|
|
41
|
+
3. For each area, report pass, concern, or unclear with exact file and symbol evidence.
|
|
42
|
+
4. State the strongest evidence-based case for accepting the contribution.
|
|
43
|
+
5. Verify the findings, remove unsupported claims, and return keep, simplify, refactor, drop, or needs evidence.
|
|
44
|
+
6. Stay read-only unless the user separately asks to implement the required changes.
|
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
type PublishedRepository,
|
|
28
28
|
type VerificationCommandPlan,
|
|
29
29
|
} from "./autoimplement-command-batches.js";
|
|
30
|
-
import
|
|
31
|
-
import
|
|
30
|
+
import { parsePlanApprovalPolicy, type PlanApprovalPolicy } from "./plan-approval.workflow.js";
|
|
31
|
+
import planChangeWorkflow, { type NormalizedPlanChangeInput } from "./plan-change.workflow.js";
|
|
32
32
|
|
|
33
33
|
export type AutoimplementInput = {
|
|
34
34
|
task: string;
|
|
@@ -44,10 +44,7 @@ export type AutoimplementInput = {
|
|
|
44
44
|
planDigest: string;
|
|
45
45
|
documents: string[];
|
|
46
46
|
};
|
|
47
|
-
approval?:
|
|
48
|
-
audience: string;
|
|
49
|
-
maxReplans: number;
|
|
50
|
-
};
|
|
47
|
+
approval?: PlanApprovalPolicy;
|
|
51
48
|
concurrency?: Partial<AutoimplementConcurrency>;
|
|
52
49
|
};
|
|
53
50
|
|
|
@@ -465,22 +462,7 @@ function parseInput(value: unknown): AutoimplementInput {
|
|
|
465
462
|
};
|
|
466
463
|
}
|
|
467
464
|
const concurrency = parseAutoimplementConcurrency(input.concurrency);
|
|
468
|
-
|
|
469
|
-
if (input.approval !== undefined) {
|
|
470
|
-
const raw = requireRecord(input.approval, "autoimplement approval");
|
|
471
|
-
const maxReplans = raw.maxReplans ?? 3;
|
|
472
|
-
if (
|
|
473
|
-
!Number.isInteger(maxReplans) ||
|
|
474
|
-
(maxReplans as number) < 1 ||
|
|
475
|
-
(maxReplans as number) > 20
|
|
476
|
-
) {
|
|
477
|
-
throw new Error("autoimplement approval maxReplans must be from 1 through 20");
|
|
478
|
-
}
|
|
479
|
-
approval = {
|
|
480
|
-
audience: requireString(raw.audience, "autoimplement approval audience"),
|
|
481
|
-
maxReplans: maxReplans as number,
|
|
482
|
-
};
|
|
483
|
-
}
|
|
465
|
+
const approval = parsePlanApprovalPolicy(input.approval);
|
|
484
466
|
return {
|
|
485
467
|
task: requireString(input.task, "autoimplement task"),
|
|
486
468
|
...(input.plan !== undefined ? { plan: input.plan } : {}),
|
|
@@ -495,7 +477,7 @@ function parseInput(value: unknown): AutoimplementInput {
|
|
|
495
477
|
merge: input.merge === true,
|
|
496
478
|
...(documents !== undefined ? { documents: [...documents] as string[] } : {}),
|
|
497
479
|
...(documentation !== undefined ? { documentation } : {}),
|
|
498
|
-
|
|
480
|
+
approval,
|
|
499
481
|
concurrency,
|
|
500
482
|
};
|
|
501
483
|
}
|
|
@@ -637,20 +619,6 @@ function currentPlan(context: WorkflowNodeContext): unknown {
|
|
|
637
619
|
return (context.input as AutoimplementInput).plan;
|
|
638
620
|
}
|
|
639
621
|
|
|
640
|
-
function currentPlanDigest(context: WorkflowNodeContext): string {
|
|
641
|
-
const adopted = context.outputs.adoptPlan as { planDigest?: unknown } | undefined;
|
|
642
|
-
if (typeof adopted?.planDigest === "string") return adopted.planDigest;
|
|
643
|
-
const documented = context.outputs.documentation as
|
|
644
|
-
| { exit?: string; output?: { planDigest?: unknown } }
|
|
645
|
-
| undefined;
|
|
646
|
-
if (documented?.exit === "ready" && typeof documented.output?.planDigest === "string") {
|
|
647
|
-
return documented.output.planDigest;
|
|
648
|
-
}
|
|
649
|
-
const plan = currentPlan(context);
|
|
650
|
-
if (plan === undefined) throw new Error("autoimplement does not have a selected plan");
|
|
651
|
-
return digest(plan);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
622
|
function blockerChallenges(context: WorkflowNodeContext): BlockerChallenge[] {
|
|
655
623
|
return context.state.steps
|
|
656
624
|
.filter((step) => step.nodeId === "challengeBlocker" && step.outcome === "ok")
|
|
@@ -688,16 +656,6 @@ function recentWorkflowAttempts(context: WorkflowNodeContext): unknown[] {
|
|
|
688
656
|
}
|
|
689
657
|
|
|
690
658
|
function latestIssue(context: WorkflowNodeContext): unknown {
|
|
691
|
-
const approval = context.outputs.approval as
|
|
692
|
-
| { exit?: string; output?: { instructions?: unknown } }
|
|
693
|
-
| undefined;
|
|
694
|
-
if (approval?.exit === "replan" && typeof approval.output?.instructions === "string") {
|
|
695
|
-
return {
|
|
696
|
-
source: "human-replan",
|
|
697
|
-
instructions: approval.output.instructions,
|
|
698
|
-
priorPlanDigest: currentPlanDigest(context),
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
659
|
const ids = [
|
|
702
660
|
"challengeBlocker",
|
|
703
661
|
"classifyImplementation",
|
|
@@ -1145,11 +1103,10 @@ function latestBlockedReason(context: WorkflowNodeContext): { reason: string; ev
|
|
|
1145
1103
|
"classifyImplementation",
|
|
1146
1104
|
"classifyVerification",
|
|
1147
1105
|
"triageReview",
|
|
1148
|
-
"
|
|
1106
|
+
"redesign",
|
|
1149
1107
|
"adoptPlan",
|
|
1150
1108
|
"findPlan",
|
|
1151
1109
|
"documentation",
|
|
1152
|
-
"approval",
|
|
1153
1110
|
];
|
|
1154
1111
|
for (let index = context.state.steps.length - 1; index >= 0; index -= 1) {
|
|
1155
1112
|
const step = context.state.steps[index];
|
|
@@ -1191,37 +1148,18 @@ export const autoimplementWorkflow = defineWorkflow({
|
|
|
1191
1148
|
};
|
|
1192
1149
|
},
|
|
1193
1150
|
}),
|
|
1194
|
-
|
|
1195
|
-
input: (context):
|
|
1151
|
+
redesign: includeWorkflow(planChangeWorkflow, {
|
|
1152
|
+
input: (context): NormalizedPlanChangeInput => {
|
|
1196
1153
|
const request = context.input as AutoimplementInput;
|
|
1197
|
-
if (request.approval === undefined) {
|
|
1198
|
-
throw new Error("autoimplement approval was entered without an approval policy");
|
|
1199
|
-
}
|
|
1200
|
-
const plan = currentPlan(context);
|
|
1201
|
-
if (plan === undefined) throw new Error("autoimplement approval is missing a plan");
|
|
1202
|
-
const revisions = context.state.steps.filter(
|
|
1203
|
-
(step) => step.nodeId === "approval/approve",
|
|
1204
|
-
).length;
|
|
1205
1154
|
return {
|
|
1206
1155
|
task: request.task,
|
|
1207
|
-
plan,
|
|
1208
|
-
planDigest: currentPlanDigest(context),
|
|
1209
|
-
audience: request.approval.audience,
|
|
1210
|
-
revision: revisions + 1,
|
|
1211
|
-
};
|
|
1212
|
-
},
|
|
1213
|
-
}),
|
|
1214
|
-
redesign: includeWorkflow({
|
|
1215
|
-
workflow: "autoplan",
|
|
1216
|
-
contract: autoplanWorkflow,
|
|
1217
|
-
input: (context): AutoplanInput => {
|
|
1218
|
-
const request = context.input as AutoimplementInput;
|
|
1219
|
-
return {
|
|
1220
|
-
problem: request.task,
|
|
1221
1156
|
...(request.scope !== undefined ? { scope: request.scope } : {}),
|
|
1222
1157
|
...(request.constraints !== undefined ? { constraints: request.constraints } : {}),
|
|
1158
|
+
...(request.repository !== undefined ? { repository: request.repository } : {}),
|
|
1159
|
+
documents: request.documents ?? request.documentation?.documents ?? [],
|
|
1223
1160
|
...(currentPlan(context) !== undefined ? { previousPlan: currentPlan(context) } : {}),
|
|
1224
1161
|
newEvidence: latestIssue(context),
|
|
1162
|
+
approval: parsePlanApprovalPolicy(request.approval),
|
|
1225
1163
|
};
|
|
1226
1164
|
},
|
|
1227
1165
|
}),
|
|
@@ -1285,40 +1223,17 @@ export const autoimplementWorkflow = defineWorkflow({
|
|
|
1285
1223
|
}),
|
|
1286
1224
|
adoptPlan: compute({
|
|
1287
1225
|
run: ({ outputs }) => {
|
|
1288
|
-
const result = includedResult(
|
|
1226
|
+
const result = includedResult(planChangeWorkflow, outputs.redesign);
|
|
1289
1227
|
if (result.exit !== "ready") throw new Error("redesign did not return a ready plan");
|
|
1290
1228
|
return {
|
|
1291
|
-
route: result.output.changed ? "document" : "blocked",
|
|
1292
1229
|
plan: result.output.plan,
|
|
1293
1230
|
planDigest: result.output.planDigest,
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
: "Redesign returned the same plan for the same unresolved evidence.",
|
|
1231
|
+
documents: result.output.documents,
|
|
1232
|
+
approval: result.output.approval,
|
|
1233
|
+
reason: "The changed plan was documented and passed its approval policy.",
|
|
1298
1234
|
};
|
|
1299
1235
|
},
|
|
1300
1236
|
}),
|
|
1301
|
-
maybeApproval: compute({
|
|
1302
|
-
run: ({ input }) => ({
|
|
1303
|
-
route: (input as AutoimplementInput).approval === undefined ? "implement" : "approve",
|
|
1304
|
-
}),
|
|
1305
|
-
}),
|
|
1306
|
-
replanGuard: compute({
|
|
1307
|
-
run: (context) => {
|
|
1308
|
-
const request = context.input as AutoimplementInput;
|
|
1309
|
-
const limit = request.approval?.maxReplans ?? 3;
|
|
1310
|
-
const replans = context.state.steps.filter(
|
|
1311
|
-
(step) => step.nodeId === "approval/replan",
|
|
1312
|
-
).length;
|
|
1313
|
-
return replans > limit
|
|
1314
|
-
? {
|
|
1315
|
-
route: "blocked",
|
|
1316
|
-
reason: `Plan approval reached the ${limit}-replan safety limit.`,
|
|
1317
|
-
evidence: context.outputs.approval,
|
|
1318
|
-
}
|
|
1319
|
-
: { route: "redesign", replans, limit };
|
|
1320
|
-
},
|
|
1321
|
-
}),
|
|
1322
1237
|
timeoutFallbackGuard: compute({
|
|
1323
1238
|
run: timeoutFallbackGuard,
|
|
1324
1239
|
}),
|
|
@@ -1808,7 +1723,7 @@ export const autoimplementWorkflow = defineWorkflow({
|
|
|
1808
1723
|
from: "prepare",
|
|
1809
1724
|
switch: {
|
|
1810
1725
|
on: "$.route",
|
|
1811
|
-
cases: { find: "findPlan", document: "documentation", ready: "
|
|
1726
|
+
cases: { find: "findPlan", document: "documentation", ready: "implement" },
|
|
1812
1727
|
},
|
|
1813
1728
|
},
|
|
1814
1729
|
{
|
|
@@ -1819,28 +1734,14 @@ export const autoimplementWorkflow = defineWorkflow({
|
|
|
1819
1734
|
from: "routeFoundPlan",
|
|
1820
1735
|
switch: {
|
|
1821
1736
|
on: "$.route",
|
|
1822
|
-
cases: { ready: "
|
|
1737
|
+
cases: { ready: "implement", document: "documentation", blocked: "blocked" },
|
|
1823
1738
|
},
|
|
1824
1739
|
},
|
|
1825
1740
|
{ from: "redesign.ready", to: "adoptPlan" },
|
|
1826
1741
|
{ from: "redesign.blocked", to: "blocked" },
|
|
1827
|
-
{
|
|
1828
|
-
|
|
1829
|
-
switch: { on: "$.route", cases: { document: "documentation", blocked: "blocked" } },
|
|
1830
|
-
},
|
|
1831
|
-
{ from: "documentation.ready", to: "maybeApproval" },
|
|
1742
|
+
{ from: "adoptPlan", to: "implement" },
|
|
1743
|
+
{ from: "documentation.ready", to: "implement" },
|
|
1832
1744
|
{ from: "documentation.blocked", to: "blocked" },
|
|
1833
|
-
{
|
|
1834
|
-
from: "maybeApproval",
|
|
1835
|
-
switch: { on: "$.route", cases: { approve: "approval", implement: "implement" } },
|
|
1836
|
-
},
|
|
1837
|
-
{ from: "approval.continue", to: "implement" },
|
|
1838
|
-
{ from: "approval.stop", to: "blocked" },
|
|
1839
|
-
{ from: "approval.replan", to: "replanGuard" },
|
|
1840
|
-
{
|
|
1841
|
-
from: "replanGuard",
|
|
1842
|
-
switch: { on: "$.route", cases: { redesign: "redesign", blocked: "blocked" } },
|
|
1843
|
-
},
|
|
1844
1745
|
{
|
|
1845
1746
|
from: "timeoutFallbackGuard",
|
|
1846
1747
|
switch: { on: "$.route", cases: { recover: "timeoutFallback", blocked: "blocked" } },
|
package/src/builtins/catalog.ts
CHANGED
|
@@ -9,12 +9,12 @@ import sanityCheckWorkflow from "./sanity-check.workflow.js";
|
|
|
9
9
|
export const builtinWorkflowCatalog = new BuiltinWorkflowCatalog([
|
|
10
10
|
{ id: "autoplan", revision: "1", definition: autoplanWorkflow },
|
|
11
11
|
{ id: "autodoc", revision: "1", definition: autodocWorkflow },
|
|
12
|
-
{ id: "autoimplement", revision: "
|
|
13
|
-
{ id: "plan-approval", revision: "
|
|
14
|
-
{ id: "sanity-check", revision: "
|
|
12
|
+
{ id: "autoimplement", revision: "8", definition: autoimplementWorkflow },
|
|
13
|
+
{ id: "plan-approval", revision: "4", definition: planApprovalWorkflow },
|
|
14
|
+
{ id: "sanity-check", revision: "3", definition: sanityCheckWorkflow },
|
|
15
15
|
{
|
|
16
16
|
id: "monitor",
|
|
17
|
-
revision: "
|
|
17
|
+
revision: "9",
|
|
18
18
|
definition: monitorWorkflow,
|
|
19
19
|
legacySources: [
|
|
20
20
|
{
|
package/src/builtins/index.ts
CHANGED
|
@@ -23,6 +23,12 @@ export {
|
|
|
23
23
|
type MonitorInput,
|
|
24
24
|
type MonitorRepairPolicy,
|
|
25
25
|
} from "./monitor.workflow.js";
|
|
26
|
+
export {
|
|
27
|
+
planChangeWorkflow,
|
|
28
|
+
type PlanChangeBlocked,
|
|
29
|
+
type PlanChangeInput,
|
|
30
|
+
type PlanChangeReady,
|
|
31
|
+
} from "./plan-change.workflow.js";
|
|
26
32
|
export { presentPlan } from "./plan-presentation.js";
|
|
27
33
|
export {
|
|
28
34
|
sanityCheckWorkflow,
|
|
@@ -38,8 +44,13 @@ export {
|
|
|
38
44
|
} from "./sanity-check.workflow.js";
|
|
39
45
|
export {
|
|
40
46
|
planApprovalWorkflow,
|
|
47
|
+
parsePlanApprovalPolicy,
|
|
41
48
|
type PlanApprovalContinue,
|
|
42
49
|
type PlanApprovalInput,
|
|
50
|
+
type PlanApprovalMode,
|
|
51
|
+
type PlanApprovalPolicy,
|
|
43
52
|
type PlanApprovalReplan,
|
|
53
|
+
type PlanApprovalResolution,
|
|
44
54
|
type PlanApprovalStop,
|
|
55
|
+
type ResolvedPlanApprovalPolicy,
|
|
45
56
|
} from "./plan-approval.workflow.js";
|