@wichayutdew/pi-workflows 1.0.0 → 1.0.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 +2 -0
- package/dist/index.js +27 -10
- package/examples/mr-comments.workflow.yaml +2 -0
- package/package.json +1 -1
- package/schemas/workflow.schema.json +10 -1
- package/src/config/ceiling.ts +4 -0
- package/src/config/types.ts +12 -1
- package/src/config/validation/permissions.ts +22 -1
- package/src/config/validation/prompt.ts +2 -0
- package/src/config/validation/shared.ts +1 -1
- package/src/engine/create-run.ts +1 -0
- package/src/engine/gate-transitions.ts +4 -1
- package/src/engine/run-advance.ts +1 -1
- package/src/engine/run-validation.ts +1 -0
- package/src/engine/state-types.ts +2 -0
- package/src/integrations/subagents/child-policy-sections.ts +13 -1
- package/src/prompt/template.ts +2 -0
package/README.md
CHANGED
|
@@ -398,6 +398,8 @@ Supported sources are:
|
|
|
398
398
|
| `verification-worker` | `repositories[].worker[].command` |
|
|
399
399
|
| `verification-reviewer` | `repositories[].reviewer[].command` |
|
|
400
400
|
| `remote-actions` | `actions[]` where `toolName` is `bash` and `input.command` is present |
|
|
401
|
+
| `remote-push` | Exact approved non-force `git push` command |
|
|
402
|
+
| `remote-drafts` | Parent-synthesized author-private review drafts |
|
|
401
403
|
|
|
402
404
|
The JSON must be the whole reviewed artifact or appear in a fenced `json`
|
|
403
405
|
block. The harness copies only exact strings into the correlated step policy.
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,8 @@ function bashWithinCeiling(requested, ceiling) {
|
|
|
29
29
|
return false;
|
|
30
30
|
const allowedRules = new Set(ceiling.allow.map(ruleKey));
|
|
31
31
|
const allowedSources = new Set(ceiling.approvedSources ?? []);
|
|
32
|
-
|
|
32
|
+
const allowedHandoffSources = new Set(ceiling.handoffSources ?? []);
|
|
33
|
+
return requested.allow.every((rule) => allowedRules.has(ruleKey(rule))) && (requested.approvedSources ?? []).every((source) => allowedSources.has(source)) && (requested.handoffSources ?? []).every((source) => allowedHandoffSources.has(source));
|
|
33
34
|
}
|
|
34
35
|
function turnBudgetErrors(subagent, ceiling, path) {
|
|
35
36
|
if (!subagent.turnBudget) {
|
|
@@ -135,6 +136,8 @@ var PROMPT_VARIABLES = new Set([
|
|
|
135
136
|
"step.id",
|
|
136
137
|
"step.title",
|
|
137
138
|
"last.summary",
|
|
139
|
+
"reviewed.artifact",
|
|
140
|
+
"reviewed.feedback",
|
|
138
141
|
"gate.feedback"
|
|
139
142
|
]);
|
|
140
143
|
function validatePromptText(text, path) {
|
|
@@ -150,7 +153,7 @@ var TOOL_PATTERN = /^[A-Za-z0-9_.:-]+$/;
|
|
|
150
153
|
var RESOURCE_SELECTOR_PATTERN = /^[A-Za-z0-9_@./:+-]+$/;
|
|
151
154
|
var MCP_SELECTOR_PATTERN = /^[A-Za-z0-9_.:-]+(?:\/[A-Za-z0-9_.:-]+)?$/;
|
|
152
155
|
var EXECUTABLE_PATTERN = /^[A-Za-z0-9_./+-]+$/;
|
|
153
|
-
var BASH_APPROVAL_SOURCE_PATTERN = /^(verification-worker|verification-reviewer|remote-actions)$/;
|
|
156
|
+
var BASH_APPROVAL_SOURCE_PATTERN = /^(verification-worker|verification-reviewer|remote-actions|remote-push|remote-drafts)$/;
|
|
154
157
|
function isJsonObject(value) {
|
|
155
158
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
156
159
|
}
|
|
@@ -215,7 +218,9 @@ function readStringList(value, path, errors, pattern) {
|
|
|
215
218
|
var BASH_APPROVAL_SOURCES = [
|
|
216
219
|
"verification-worker",
|
|
217
220
|
"verification-reviewer",
|
|
218
|
-
"remote-actions"
|
|
221
|
+
"remote-actions",
|
|
222
|
+
"remote-push",
|
|
223
|
+
"remote-drafts"
|
|
219
224
|
];
|
|
220
225
|
function isBashApprovalSource(value) {
|
|
221
226
|
return BASH_APPROVAL_SOURCES.some((source) => source === value);
|
|
@@ -282,7 +287,7 @@ function parseBashPermission(value, path, errors) {
|
|
|
282
287
|
errors.push(`${path}: expected an object`);
|
|
283
288
|
return { ...EMPTY_PERMISSIONS.bash, allow: [] };
|
|
284
289
|
}
|
|
285
|
-
rejectUnknownKeys(value, ["mode", "allow", "approvedSources"], path, errors);
|
|
290
|
+
rejectUnknownKeys(value, ["mode", "allow", "approvedSources", "handoffSources"], path, errors);
|
|
286
291
|
const mode = readString(value.mode, `${path}.mode`, errors);
|
|
287
292
|
const isValidMode = mode === "deny" || mode === "read-only" || mode === "allow-list" || mode === "unrestricted";
|
|
288
293
|
if (!isValidMode) {
|
|
@@ -293,6 +298,7 @@ function parseBashPermission(value, path, errors) {
|
|
|
293
298
|
errors.push(`${path}.allow: expected an array`);
|
|
294
299
|
}
|
|
295
300
|
const approvedSources = (value.approvedSources === undefined ? [] : readStringList(value.approvedSources, `${path}.approvedSources`, errors, BASH_APPROVAL_SOURCE_PATTERN)).filter(isBashApprovalSource);
|
|
301
|
+
const handoffSources = value.handoffSources === undefined ? [] : readStringList(value.handoffSources, `${path}.handoffSources`, errors, /^(verification-worker|verification-reviewer)$/);
|
|
296
302
|
const normalizedMode = isValidMode ? mode : "deny";
|
|
297
303
|
if (normalizedMode !== "allow-list" && allow.length > 0) {
|
|
298
304
|
errors.push(`${path}.allow: only valid when mode is "allow-list"`);
|
|
@@ -303,10 +309,14 @@ function parseBashPermission(value, path, errors) {
|
|
|
303
309
|
if (normalizedMode !== "allow-list" && approvedSources.length > 0) {
|
|
304
310
|
errors.push(`${path}.approvedSources: only valid when mode is "allow-list"`);
|
|
305
311
|
}
|
|
312
|
+
if (normalizedMode !== "allow-list" && handoffSources.length > 0) {
|
|
313
|
+
errors.push(`${path}.handoffSources: only valid when mode is "allow-list"`);
|
|
314
|
+
}
|
|
306
315
|
return {
|
|
307
316
|
mode: normalizedMode,
|
|
308
317
|
allow,
|
|
309
|
-
...approvedSources.length > 0 ? { approvedSources } : {}
|
|
318
|
+
...approvedSources.length > 0 ? { approvedSources } : {},
|
|
319
|
+
...handoffSources.length > 0 ? { handoffSources } : {}
|
|
310
320
|
};
|
|
311
321
|
}
|
|
312
322
|
function parsePermissions(value, path, errors) {
|
|
@@ -2212,9 +2222,10 @@ var isStepPermissions = (value) => {
|
|
|
2212
2222
|
const bashRules = Array.isArray(bash.allow) ? bash.allow : undefined;
|
|
2213
2223
|
const isValidMode = bash.mode === "deny" || bash.mode === "read-only" || bash.mode === "allow-list" || bash.mode === "unrestricted";
|
|
2214
2224
|
const hasValidRules = bashRules !== undefined && bashRules.every((rule) => isRecord(rule) && typeof rule.executable === "string" && isStringArray(rule.argsPrefix));
|
|
2215
|
-
const hasValidApprovedSources = bash.approvedSources === undefined || isStringArray(bash.approvedSources) && new Set(bash.approvedSources).size === bash.approvedSources.length && bash.approvedSources.every((source) => source === "verification-worker" || source === "verification-reviewer" || source === "remote-actions");
|
|
2225
|
+
const hasValidApprovedSources = bash.approvedSources === undefined || isStringArray(bash.approvedSources) && new Set(bash.approvedSources).size === bash.approvedSources.length && bash.approvedSources.every((source) => source === "verification-worker" || source === "verification-reviewer" || source === "remote-actions" || source === "remote-push" || source === "remote-drafts");
|
|
2226
|
+
const hasValidHandoffSources = bash.handoffSources === undefined || isStringArray(bash.handoffSources) && new Set(bash.handoffSources).size === bash.handoffSources.length && bash.handoffSources.every((source) => source === "verification-worker" || source === "verification-reviewer");
|
|
2216
2227
|
const hasValidApprovalShape = bash.mode === "allow-list" ? (bashRules?.length ?? 0) > 0 || Array.isArray(bash.approvedSources) && bash.approvedSources.length > 0 : bash.approvedSources === undefined;
|
|
2217
|
-
return isStringArray(value.tools) && isStringArray(value.mcp) && isStringArray(value.extensions) && isStringArray(value.skills) && isValidMode && hasValidRules && hasValidApprovedSources && hasValidApprovalShape;
|
|
2228
|
+
return isStringArray(value.tools) && isStringArray(value.mcp) && isStringArray(value.extensions) && isStringArray(value.skills) && isValidMode && hasValidRules && hasValidApprovedSources && hasValidHandoffSources && hasValidApprovalShape;
|
|
2218
2229
|
};
|
|
2219
2230
|
var parsePermissions2 = (value) => {
|
|
2220
2231
|
if (!isStepPermissions(value.permissions)) {
|
|
@@ -4238,6 +4249,7 @@ var createRun = (workflow, input, baselineTools, runId, now) => {
|
|
|
4238
4249
|
startedAt: now,
|
|
4239
4250
|
updatedAt: now,
|
|
4240
4251
|
reviewedArtifact: "",
|
|
4252
|
+
reviewedFeedback: "",
|
|
4241
4253
|
stepHandoff: "",
|
|
4242
4254
|
lastSummary: "",
|
|
4243
4255
|
gateFeedback: ""
|
|
@@ -4260,7 +4272,7 @@ var isWorkflowRun = (value) => {
|
|
|
4260
4272
|
const hasValidRequiredFields = value.stateVersion === RUN_STATE_VERSION && typeof value.runId === "string" && typeof value.workflowId === "string" && typeof value.workflowDigest === "string" && typeof value.input === "string" && isWorkflowRunStatus(value.status) && typeof value.currentStepId === "string" && typeof value.currentStepDigest === "string" && Array.isArray(value.baselineTools) && value.baselineTools.every((tool) => typeof tool === "string") && Array.isArray(value.history) && value.history.every(isStepHistoryEntry) && isVisitCounts(value.visits) && typeof value.startedAt === "number" && typeof value.updatedAt === "number" && typeof value.lastSummary === "string" && typeof value.gateFeedback === "string";
|
|
4261
4273
|
if (!hasValidRequiredFields)
|
|
4262
4274
|
return false;
|
|
4263
|
-
const hasValidOptionalFields = isOptionalString(value.reviewedArtifact) && isOptionalString(value.stepHandoff) && isOptionalString(value.pauseReason) && isOptionalString(value.failedStepId) && (value.pausedFrom === undefined || value.pausedFrom === "running" || value.pausedFrom === "awaiting-gate");
|
|
4275
|
+
const hasValidOptionalFields = isOptionalString(value.reviewedArtifact) && isOptionalString(value.reviewedFeedback) && isOptionalString(value.stepHandoff) && isOptionalString(value.pauseReason) && isOptionalString(value.failedStepId) && (value.pausedFrom === undefined || value.pausedFrom === "running" || value.pausedFrom === "awaiting-gate");
|
|
4264
4276
|
if (!hasValidOptionalFields)
|
|
4265
4277
|
return false;
|
|
4266
4278
|
const pendingGate = value.pendingGate;
|
|
@@ -4426,7 +4438,7 @@ var advanceRun = (workflow, run, outcome, summary, now) => {
|
|
|
4426
4438
|
stepHandoff: summary,
|
|
4427
4439
|
lastSummary: summary,
|
|
4428
4440
|
gateFeedback: "",
|
|
4429
|
-
...nextStep.gate ? { reviewedArtifact: "" } : {}
|
|
4441
|
+
...nextStep.gate ? { reviewedArtifact: "", reviewedFeedback: "" } : {}
|
|
4430
4442
|
}, now);
|
|
4431
4443
|
};
|
|
4432
4444
|
|
|
@@ -4489,7 +4501,10 @@ var resolveGate = (workflow, run, resolution, now) => {
|
|
|
4489
4501
|
const runnableRun = withRunUpdate(run, {
|
|
4490
4502
|
status: "running",
|
|
4491
4503
|
pendingGate: undefined,
|
|
4492
|
-
...resolution.approved ? {
|
|
4504
|
+
...resolution.approved ? {
|
|
4505
|
+
reviewedArtifact: pendingGate.artifact,
|
|
4506
|
+
reviewedFeedback: resolution.feedback
|
|
4507
|
+
} : {},
|
|
4493
4508
|
pausedFrom: undefined,
|
|
4494
4509
|
pauseReason: undefined,
|
|
4495
4510
|
gateFeedback: resolution.feedback
|
|
@@ -5029,6 +5044,8 @@ function createTemplateValues({
|
|
|
5029
5044
|
"step.id": run.currentStepId,
|
|
5030
5045
|
"step.title": step.title,
|
|
5031
5046
|
"last.summary": currentStepHandoff(run),
|
|
5047
|
+
"reviewed.artifact": run.reviewedArtifact ?? "",
|
|
5048
|
+
"reviewed.feedback": run.reviewedFeedback ?? "",
|
|
5032
5049
|
"gate.feedback": run.gateFeedback
|
|
5033
5050
|
};
|
|
5034
5051
|
}
|
|
@@ -85,6 +85,7 @@ steps:
|
|
|
85
85
|
skills: [superpowers:test-driven-development]
|
|
86
86
|
transitions:
|
|
87
87
|
ready: verify
|
|
88
|
+
retry: implement
|
|
88
89
|
blocked: $pause
|
|
89
90
|
verify:
|
|
90
91
|
title: Verify the result
|
|
@@ -112,4 +113,5 @@ steps:
|
|
|
112
113
|
transitions:
|
|
113
114
|
passed: $done
|
|
114
115
|
failed: implement
|
|
116
|
+
retry: verify
|
|
115
117
|
blocked: $pause
|
package/package.json
CHANGED
|
@@ -223,9 +223,18 @@
|
|
|
223
223
|
"enum": [
|
|
224
224
|
"verification-worker",
|
|
225
225
|
"verification-reviewer",
|
|
226
|
-
"remote-actions"
|
|
226
|
+
"remote-actions",
|
|
227
|
+
"remote-push",
|
|
228
|
+
"remote-drafts"
|
|
227
229
|
]
|
|
228
230
|
}
|
|
231
|
+
},
|
|
232
|
+
"handoffSources": {
|
|
233
|
+
"type": "array",
|
|
234
|
+
"uniqueItems": true,
|
|
235
|
+
"items": {
|
|
236
|
+
"enum": ["verification-worker", "verification-reviewer"]
|
|
237
|
+
}
|
|
229
238
|
}
|
|
230
239
|
}
|
|
231
240
|
},
|
package/src/config/ceiling.ts
CHANGED
|
@@ -33,10 +33,14 @@ function bashWithinCeiling(
|
|
|
33
33
|
|
|
34
34
|
const allowedRules = new Set(ceiling.allow.map(ruleKey));
|
|
35
35
|
const allowedSources = new Set(ceiling.approvedSources ?? []);
|
|
36
|
+
const allowedHandoffSources = new Set(ceiling.handoffSources ?? []);
|
|
36
37
|
return (
|
|
37
38
|
requested.allow.every((rule) => allowedRules.has(ruleKey(rule))) &&
|
|
38
39
|
(requested.approvedSources ?? []).every((source) =>
|
|
39
40
|
allowedSources.has(source),
|
|
41
|
+
) &&
|
|
42
|
+
(requested.handoffSources ?? []).every((source) =>
|
|
43
|
+
allowedHandoffSources.has(source),
|
|
40
44
|
)
|
|
41
45
|
);
|
|
42
46
|
}
|
package/src/config/types.ts
CHANGED
|
@@ -11,7 +11,11 @@ export type StepTarget = string;
|
|
|
11
11
|
|
|
12
12
|
export type BashMode = 'deny' | 'read-only' | 'allow-list' | 'unrestricted';
|
|
13
13
|
export type BashApprovalSource =
|
|
14
|
-
|
|
14
|
+
| 'verification-worker'
|
|
15
|
+
| 'verification-reviewer'
|
|
16
|
+
| 'remote-actions'
|
|
17
|
+
| 'remote-push'
|
|
18
|
+
| 'remote-drafts';
|
|
15
19
|
|
|
16
20
|
export type BashRule = {
|
|
17
21
|
readonly executable: string;
|
|
@@ -26,6 +30,13 @@ export type BashPermission = {
|
|
|
26
30
|
* They supplement `allow` only inside the correlated step execution.
|
|
27
31
|
*/
|
|
28
32
|
readonly approvedSources?: ReadonlyArray<BashApprovalSource>;
|
|
33
|
+
/**
|
|
34
|
+
* Sources whose command-only retry handoffs may refine an already approved
|
|
35
|
+
* command. The original reviewed identity and safety envelope remain fixed.
|
|
36
|
+
*/
|
|
37
|
+
readonly handoffSources?: ReadonlyArray<
|
|
38
|
+
'verification-worker' | 'verification-reviewer'
|
|
39
|
+
>;
|
|
29
40
|
};
|
|
30
41
|
|
|
31
42
|
export type StepPermissions = {
|
|
@@ -24,6 +24,8 @@ const BASH_APPROVAL_SOURCES = [
|
|
|
24
24
|
'verification-worker',
|
|
25
25
|
'verification-reviewer',
|
|
26
26
|
'remote-actions',
|
|
27
|
+
'remote-push',
|
|
28
|
+
'remote-drafts',
|
|
27
29
|
] as const satisfies ReadonlyArray<BashApprovalSource>;
|
|
28
30
|
|
|
29
31
|
function isBashApprovalSource(value: string): value is BashApprovalSource {
|
|
@@ -129,7 +131,12 @@ function parseBashPermission(
|
|
|
129
131
|
errors.push(`${path}: expected an object`);
|
|
130
132
|
return { ...EMPTY_PERMISSIONS.bash, allow: [] };
|
|
131
133
|
}
|
|
132
|
-
rejectUnknownKeys(
|
|
134
|
+
rejectUnknownKeys(
|
|
135
|
+
value,
|
|
136
|
+
['mode', 'allow', 'approvedSources', 'handoffSources'],
|
|
137
|
+
path,
|
|
138
|
+
errors,
|
|
139
|
+
);
|
|
133
140
|
|
|
134
141
|
const mode = readString(value.mode, `${path}.mode`, errors);
|
|
135
142
|
const isValidMode =
|
|
@@ -162,6 +169,16 @@ function parseBashPermission(
|
|
|
162
169
|
BASH_APPROVAL_SOURCE_PATTERN,
|
|
163
170
|
)
|
|
164
171
|
).filter(isBashApprovalSource);
|
|
172
|
+
const handoffSources = (
|
|
173
|
+
value.handoffSources === undefined
|
|
174
|
+
? []
|
|
175
|
+
: readStringList(
|
|
176
|
+
value.handoffSources,
|
|
177
|
+
`${path}.handoffSources`,
|
|
178
|
+
errors,
|
|
179
|
+
/^(verification-worker|verification-reviewer)$/,
|
|
180
|
+
)
|
|
181
|
+
) as Array<'verification-worker' | 'verification-reviewer'>;
|
|
165
182
|
|
|
166
183
|
const normalizedMode: BashMode = isValidMode ? mode : 'deny';
|
|
167
184
|
if (normalizedMode !== 'allow-list' && allow.length > 0) {
|
|
@@ -181,11 +198,15 @@ function parseBashPermission(
|
|
|
181
198
|
`${path}.approvedSources: only valid when mode is "allow-list"`,
|
|
182
199
|
);
|
|
183
200
|
}
|
|
201
|
+
if (normalizedMode !== 'allow-list' && handoffSources.length > 0) {
|
|
202
|
+
errors.push(`${path}.handoffSources: only valid when mode is "allow-list"`);
|
|
203
|
+
}
|
|
184
204
|
|
|
185
205
|
return {
|
|
186
206
|
mode: normalizedMode,
|
|
187
207
|
allow,
|
|
188
208
|
...(approvedSources.length > 0 ? { approvedSources } : {}),
|
|
209
|
+
...(handoffSources.length > 0 ? { handoffSources } : {}),
|
|
189
210
|
};
|
|
190
211
|
}
|
|
191
212
|
|
|
@@ -13,7 +13,7 @@ export const RESOURCE_SELECTOR_PATTERN = /^[A-Za-z0-9_@./:+-]+$/;
|
|
|
13
13
|
export const MCP_SELECTOR_PATTERN = /^[A-Za-z0-9_.:-]+(?:\/[A-Za-z0-9_.:-]+)?$/;
|
|
14
14
|
export const EXECUTABLE_PATTERN = /^[A-Za-z0-9_./+-]+$/;
|
|
15
15
|
export const BASH_APPROVAL_SOURCE_PATTERN =
|
|
16
|
-
/^(verification-worker|verification-reviewer|remote-actions)$/;
|
|
16
|
+
/^(verification-worker|verification-reviewer|remote-actions|remote-push|remote-drafts)$/;
|
|
17
17
|
|
|
18
18
|
type StringOptions = {
|
|
19
19
|
readonly pattern?: RegExp;
|
package/src/engine/create-run.ts
CHANGED
|
@@ -169,7 +169,10 @@ export const resolveGate = (
|
|
|
169
169
|
status: 'running',
|
|
170
170
|
pendingGate: undefined,
|
|
171
171
|
...(resolution.approved
|
|
172
|
-
? {
|
|
172
|
+
? {
|
|
173
|
+
reviewedArtifact: pendingGate.artifact,
|
|
174
|
+
reviewedFeedback: resolution.feedback,
|
|
175
|
+
}
|
|
173
176
|
: {}),
|
|
174
177
|
pausedFrom: undefined,
|
|
175
178
|
pauseReason: undefined,
|
|
@@ -115,7 +115,7 @@ export const advanceRun = (
|
|
|
115
115
|
stepHandoff: summary,
|
|
116
116
|
lastSummary: summary,
|
|
117
117
|
gateFeedback: '',
|
|
118
|
-
...(nextStep.gate ? { reviewedArtifact: '' } : {}),
|
|
118
|
+
...(nextStep.gate ? { reviewedArtifact: '', reviewedFeedback: '' } : {}),
|
|
119
119
|
},
|
|
120
120
|
now,
|
|
121
121
|
);
|
|
@@ -109,6 +109,7 @@ export const isWorkflowRun = (value: unknown): value is WorkflowRun => {
|
|
|
109
109
|
|
|
110
110
|
const hasValidOptionalFields =
|
|
111
111
|
isOptionalString(value.reviewedArtifact) &&
|
|
112
|
+
isOptionalString(value.reviewedFeedback) &&
|
|
112
113
|
isOptionalString(value.stepHandoff) &&
|
|
113
114
|
isOptionalString(value.pauseReason) &&
|
|
114
115
|
isOptionalString(value.failedStepId) &&
|
|
@@ -49,6 +49,8 @@ export type WorkflowRun = {
|
|
|
49
49
|
* source from which reviewed Bash capabilities may be derived.
|
|
50
50
|
*/
|
|
51
51
|
readonly reviewedArtifact?: string | undefined;
|
|
52
|
+
/** Final feedback paired with the immutable approved artifact. */
|
|
53
|
+
readonly reviewedFeedback?: string | undefined;
|
|
52
54
|
/**
|
|
53
55
|
* Input inherited from the previous completed step. Unlike `lastSummary`,
|
|
54
56
|
* this survives a paused attempt of the current step.
|
|
@@ -46,7 +46,18 @@ const isStepPermissions = (value: unknown): value is StepPermissions => {
|
|
|
46
46
|
(source) =>
|
|
47
47
|
source === 'verification-worker' ||
|
|
48
48
|
source === 'verification-reviewer' ||
|
|
49
|
-
source === 'remote-actions'
|
|
49
|
+
source === 'remote-actions' ||
|
|
50
|
+
source === 'remote-push' ||
|
|
51
|
+
source === 'remote-drafts',
|
|
52
|
+
));
|
|
53
|
+
const hasValidHandoffSources =
|
|
54
|
+
bash.handoffSources === undefined ||
|
|
55
|
+
(isStringArray(bash.handoffSources) &&
|
|
56
|
+
new Set(bash.handoffSources).size === bash.handoffSources.length &&
|
|
57
|
+
bash.handoffSources.every(
|
|
58
|
+
(source) =>
|
|
59
|
+
source === 'verification-worker' ||
|
|
60
|
+
source === 'verification-reviewer',
|
|
50
61
|
));
|
|
51
62
|
const hasValidApprovalShape =
|
|
52
63
|
bash.mode === 'allow-list'
|
|
@@ -62,6 +73,7 @@ const isStepPermissions = (value: unknown): value is StepPermissions => {
|
|
|
62
73
|
isValidMode &&
|
|
63
74
|
hasValidRules &&
|
|
64
75
|
hasValidApprovedSources &&
|
|
76
|
+
hasValidHandoffSources &&
|
|
65
77
|
hasValidApprovalShape
|
|
66
78
|
);
|
|
67
79
|
};
|
package/src/prompt/template.ts
CHANGED
|
@@ -76,6 +76,8 @@ export function createTemplateValues({
|
|
|
76
76
|
'step.id': run.currentStepId,
|
|
77
77
|
'step.title': step.title,
|
|
78
78
|
'last.summary': currentStepHandoff(run),
|
|
79
|
+
'reviewed.artifact': run.reviewedArtifact ?? '',
|
|
80
|
+
'reviewed.feedback': run.reviewedFeedback ?? '',
|
|
79
81
|
'gate.feedback': run.gateFeedback,
|
|
80
82
|
};
|
|
81
83
|
}
|