@atolis-hq/wake 0.2.38 → 0.2.39
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/dist/src/adapters/claude/claude-runner.js +1 -0
- package/dist/src/adapters/codex/codex-runner.js +1 -0
- package/dist/src/adapters/cursor/cursor-runner.js +1 -0
- package/dist/src/adapters/github/github-issues-work-source.js +69 -0
- package/dist/src/adapters/runner/stage-prompt.js +2 -0
- package/dist/src/core/policy-engine.js +10 -0
- package/dist/src/core/projection-updater.js +7 -0
- package/dist/src/core/tick-runner.js +13 -2
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
- package/prompts/refine.md +1 -0
|
@@ -321,6 +321,7 @@ export function createClaudeRunner(options) {
|
|
|
321
321
|
stderr: result.stderr,
|
|
322
322
|
raw: parsed,
|
|
323
323
|
skipApproval: stagePrompt.skipApproval,
|
|
324
|
+
allowAutoApproval: stagePrompt.allowAutoApproval,
|
|
324
325
|
...(promptTranscriptPath === undefined ? {} : { promptTranscriptPath }),
|
|
325
326
|
...(responseTranscriptPath === undefined ? {} : { responseTranscriptPath }),
|
|
326
327
|
...(sandboxLog?.metadata ?? {}),
|
|
@@ -359,6 +359,7 @@ export function createCodexRunner(options) {
|
|
|
359
359
|
.filter((line) => line.trim().length > 0)
|
|
360
360
|
.map((line) => JSON.parse(line)),
|
|
361
361
|
skipApproval: stagePrompt.skipApproval,
|
|
362
|
+
allowAutoApproval: stagePrompt.allowAutoApproval,
|
|
362
363
|
...(promptTranscriptPath === undefined ? {} : { promptTranscriptPath }),
|
|
363
364
|
...(responseTranscriptPath === undefined ? {} : { responseTranscriptPath }),
|
|
364
365
|
...(sandboxLog?.metadata ?? {}),
|
|
@@ -324,6 +324,7 @@ export function createCursorRunner(options) {
|
|
|
324
324
|
stderr: result.stderr,
|
|
325
325
|
raw: parsed,
|
|
326
326
|
skipApproval: stagePrompt.skipApproval,
|
|
327
|
+
allowAutoApproval: stagePrompt.allowAutoApproval,
|
|
327
328
|
...(promptTranscriptPath === undefined ? {} : { promptTranscriptPath }),
|
|
328
329
|
...(responseTranscriptPath === undefined ? {} : { responseTranscriptPath }),
|
|
329
330
|
...(sandboxLog?.metadata ?? {}),
|
|
@@ -18,6 +18,8 @@ const pollOverlapMs = 60 * 60 * 1000;
|
|
|
18
18
|
// expectedEcho bookkeeping surviving a crash (#145).
|
|
19
19
|
const wakeCommentMarker = '<!-- wake:agent -->';
|
|
20
20
|
const githubSource = 'github';
|
|
21
|
+
const autoApprovalLabel = 'wake:auto';
|
|
22
|
+
const autoApprovalCommands = new Set(['yolo', 'autoapprove']);
|
|
21
23
|
export function wakeIdempotencyMarker(idempotencyKey) {
|
|
22
24
|
return typeof idempotencyKey === 'string'
|
|
23
25
|
? `<!-- wake:idempotency ${idempotencyKey} -->`
|
|
@@ -136,6 +138,31 @@ function issueAssignees(issue) {
|
|
|
136
138
|
.map((assignee) => assignee.login)
|
|
137
139
|
.filter((login) => typeof login === 'string');
|
|
138
140
|
}
|
|
141
|
+
function autoApprovalCommandName(body) {
|
|
142
|
+
for (const line of (body ?? '').split(/\r?\n/)) {
|
|
143
|
+
const match = /^\/([A-Za-z0-9_.-]+)\b/.exec(line.trim());
|
|
144
|
+
if (match?.[1] !== undefined && autoApprovalCommands.has(match[1].toLowerCase())) {
|
|
145
|
+
return match[1].toLowerCase();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function issueWithAutoApprovalLabel(issue) {
|
|
151
|
+
return {
|
|
152
|
+
...issue,
|
|
153
|
+
labels: [...issueLabels(issue), autoApprovalLabel],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async function ensureAutoApprovalLabel(input) {
|
|
157
|
+
if (issueLabels(input.issue).includes(autoApprovalLabel)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
await input.setLabels(input.owner, input.repoName, input.issueNumber, [
|
|
161
|
+
...issueLabels(input.issue),
|
|
162
|
+
autoApprovalLabel,
|
|
163
|
+
]);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
139
166
|
function isExpectedLabelEcho(issue, local) {
|
|
140
167
|
if (local === null || local.wake.expectedEcho.labels.length === 0) {
|
|
141
168
|
return false;
|
|
@@ -342,6 +369,7 @@ export function createGitHubIssuesWorkSource(deps) {
|
|
|
342
369
|
}
|
|
343
370
|
const comments = await deps.client.listComments(owner, repo, input.projection.issue.number, deps.config.sources.github.polling.commentPageSize);
|
|
344
371
|
let latestCommentRevision = '';
|
|
372
|
+
let autoApprovalLabelAdded = false;
|
|
345
373
|
for (const comment of comments) {
|
|
346
374
|
if (comment.updated_at > latestCommentRevision) {
|
|
347
375
|
latestCommentRevision = comment.updated_at;
|
|
@@ -353,6 +381,19 @@ export function createGitHubIssuesWorkSource(deps) {
|
|
|
353
381
|
if (input.projection.wake.expectedEcho.commentIds.includes(String(comment.id))) {
|
|
354
382
|
continue;
|
|
355
383
|
}
|
|
384
|
+
if (!autoApprovalLabelAdded &&
|
|
385
|
+
autoApprovalCommandName(comment.body) !== null &&
|
|
386
|
+
comment.user?.type !== 'Bot' &&
|
|
387
|
+
!(comment.body ?? '').includes(wakeCommentMarker) &&
|
|
388
|
+
(deps.selfLogin === undefined || comment.user?.login !== deps.selfLogin)) {
|
|
389
|
+
autoApprovalLabelAdded = await ensureAutoApprovalLabel({
|
|
390
|
+
owner,
|
|
391
|
+
repoName: repo,
|
|
392
|
+
issueNumber: input.projection.issue.number,
|
|
393
|
+
issue,
|
|
394
|
+
setLabels: deps.client.setLabels,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
356
397
|
events.push(normalizeTicketCommentEvent({
|
|
357
398
|
repo: repoRef,
|
|
358
399
|
issueNumber: input.projection.issue.number,
|
|
@@ -362,6 +403,13 @@ export function createGitHubIssuesWorkSource(deps) {
|
|
|
362
403
|
...(known?.updatedAt === undefined ? {} : { existingUpdatedAt: known.updatedAt }),
|
|
363
404
|
}));
|
|
364
405
|
}
|
|
406
|
+
if (autoApprovalLabelAdded) {
|
|
407
|
+
events.push(normalizeTicketUpsert({
|
|
408
|
+
repo: repoRef,
|
|
409
|
+
issue: issueWithAutoApprovalLabel(issue),
|
|
410
|
+
ingestedAt,
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
365
413
|
return {
|
|
366
414
|
events,
|
|
367
415
|
sourceRevision: latestCommentRevision.length > 0
|
|
@@ -408,6 +456,7 @@ export function createGitHubIssuesWorkSource(deps) {
|
|
|
408
456
|
}));
|
|
409
457
|
}
|
|
410
458
|
const comments = await deps.client.listComments(owner, repo, issue.number, deps.config.sources.github.polling.commentPageSize);
|
|
459
|
+
let autoApprovalLabelAdded = false;
|
|
411
460
|
for (const comment of comments) {
|
|
412
461
|
const known = local?.comments.find((entry) => entry.id === String(comment.id));
|
|
413
462
|
if (known?.updatedAt === comment.updated_at) {
|
|
@@ -416,6 +465,19 @@ export function createGitHubIssuesWorkSource(deps) {
|
|
|
416
465
|
if (local?.wake.expectedEcho.commentIds.includes(String(comment.id)) === true) {
|
|
417
466
|
continue;
|
|
418
467
|
}
|
|
468
|
+
if (!autoApprovalLabelAdded &&
|
|
469
|
+
autoApprovalCommandName(comment.body) !== null &&
|
|
470
|
+
comment.user?.type !== 'Bot' &&
|
|
471
|
+
!(comment.body ?? '').includes(wakeCommentMarker) &&
|
|
472
|
+
(deps.selfLogin === undefined || comment.user?.login !== deps.selfLogin)) {
|
|
473
|
+
autoApprovalLabelAdded = await ensureAutoApprovalLabel({
|
|
474
|
+
owner,
|
|
475
|
+
repoName: repo,
|
|
476
|
+
issueNumber: issue.number,
|
|
477
|
+
issue,
|
|
478
|
+
setLabels: deps.client.setLabels,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
419
481
|
events.push(normalizeTicketCommentEvent({
|
|
420
482
|
repo: repoRef,
|
|
421
483
|
issueNumber: issue.number,
|
|
@@ -425,6 +487,13 @@ export function createGitHubIssuesWorkSource(deps) {
|
|
|
425
487
|
...(known?.updatedAt === undefined ? {} : { existingUpdatedAt: known.updatedAt }),
|
|
426
488
|
}));
|
|
427
489
|
}
|
|
490
|
+
if (autoApprovalLabelAdded) {
|
|
491
|
+
events.push(normalizeTicketUpsert({
|
|
492
|
+
repo: repoRef,
|
|
493
|
+
issue: issueWithAutoApprovalLabel(issue),
|
|
494
|
+
ingestedAt,
|
|
495
|
+
}));
|
|
496
|
+
}
|
|
428
497
|
}
|
|
429
498
|
await deps.stateStore.writeSourceState({
|
|
430
499
|
schemaVersion: 1,
|
|
@@ -183,6 +183,7 @@ export async function buildStagePrompt(input) {
|
|
|
183
183
|
Object.assign(context, input.contextOverrides);
|
|
184
184
|
}
|
|
185
185
|
const skipApproval = template.frontmatter.skipApproval === 'true';
|
|
186
|
+
const allowAutoApproval = !skipApproval && template.frontmatter.allowAutoApproval === 'true';
|
|
186
187
|
const permissionMode = template.frontmatter.permissionMode;
|
|
187
188
|
const commentsToAddress = newCommentsSinceLastRun(input.projection);
|
|
188
189
|
const priorComments = previousCommentsThroughLastRun(input.projection);
|
|
@@ -237,6 +238,7 @@ export async function buildStagePrompt(input) {
|
|
|
237
238
|
value: template.frontmatter.maxTurns,
|
|
238
239
|
}),
|
|
239
240
|
skipApproval,
|
|
241
|
+
allowAutoApproval,
|
|
240
242
|
...(permissionMode === undefined ? {} : { permissionMode }),
|
|
241
243
|
};
|
|
242
244
|
}
|
|
@@ -23,6 +23,7 @@ const approvedCommandPattern = /^\/approved\b/i;
|
|
|
23
23
|
const changesCommandPattern = /^\/changes\b/i;
|
|
24
24
|
const prReviewApprovalMarker = '<!-- wake:pr-review-approved -->';
|
|
25
25
|
const prReviewChangesMarker = '<!-- wake:pr-review-changes-requested -->';
|
|
26
|
+
const autoApprovalLabel = 'wake:auto';
|
|
26
27
|
// The action Wake runs when a correlated PR gets new reviewer feedback while
|
|
27
28
|
// the work item is awaiting approval. Not configurable per workflow: it's a
|
|
28
29
|
// lateral response to a PR surface, not a workflow stage.
|
|
@@ -175,6 +176,15 @@ export function createPolicyEngine() {
|
|
|
175
176
|
if (pendingAction === undefined) {
|
|
176
177
|
return null;
|
|
177
178
|
}
|
|
179
|
+
if (context.pendingApprovalAllowAutoApproval === true &&
|
|
180
|
+
issue.issue.labels.includes(autoApprovalLabel)) {
|
|
181
|
+
return {
|
|
182
|
+
approved: true,
|
|
183
|
+
pendingAction,
|
|
184
|
+
automatic: true,
|
|
185
|
+
reason: 'Issue has wake:auto and the pending action prompt declared allowAutoApproval: true.',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
178
188
|
if (latestComment?.isBotAuthored === true &&
|
|
179
189
|
latestComment.resourceUri !== undefined &&
|
|
180
190
|
latestComment.body.includes(prReviewApprovalMarker)) {
|
|
@@ -244,6 +244,9 @@ async function applyEvent(current, event, ctx, config) {
|
|
|
244
244
|
...(payload.sentinel === 'AWAITING_APPROVAL' && payload.action !== undefined
|
|
245
245
|
? { pendingApprovalAction: payload.action }
|
|
246
246
|
: {}),
|
|
247
|
+
...(payload.sentinel === 'AWAITING_APPROVAL'
|
|
248
|
+
? { pendingApprovalAllowAutoApproval: payload.allowAutoApproval === true }
|
|
249
|
+
: {}),
|
|
247
250
|
...(payload.executionOutcome !== undefined
|
|
248
251
|
? { lastExecutionOutcome: payload.executionOutcome }
|
|
249
252
|
: {}),
|
|
@@ -257,6 +260,10 @@ async function applyEvent(current, event, ctx, config) {
|
|
|
257
260
|
else if (payload.sentinel !== undefined) {
|
|
258
261
|
delete nextContext.blockedFromStage;
|
|
259
262
|
}
|
|
263
|
+
if (payload.sentinel !== 'AWAITING_APPROVAL' && !isCompletedCustomCommand) {
|
|
264
|
+
delete nextContext.pendingApprovalAction;
|
|
265
|
+
delete nextContext.pendingApprovalAllowAutoApproval;
|
|
266
|
+
}
|
|
260
267
|
return parseIssueStateRecord({
|
|
261
268
|
...current,
|
|
262
269
|
context: nextContext,
|
|
@@ -622,6 +622,7 @@ export function createTickRunner(deps) {
|
|
|
622
622
|
else if (approvalResolution.approved) {
|
|
623
623
|
const approvalId = `approval-${candidate.issue.number}-${deps.clock.now().getTime()}`;
|
|
624
624
|
const approvedAt = deps.clock.now().toISOString();
|
|
625
|
+
const automaticApproval = approvalResolution.automatic === true;
|
|
625
626
|
const nextStage = lifecycle.nextStageFromSentinel(candidate.wake.stage, 'DONE', workflow);
|
|
626
627
|
if (nextStage === null) {
|
|
627
628
|
return { status: 'idle' };
|
|
@@ -646,8 +647,17 @@ export function createTickRunner(deps) {
|
|
|
646
647
|
sentinel: 'DONE',
|
|
647
648
|
nextStage,
|
|
648
649
|
runId: approvalId,
|
|
649
|
-
reason: 'human:approved',
|
|
650
|
-
handledCommentId: latestHumanCommentId(candidate),
|
|
650
|
+
reason: automaticApproval ? 'auto:approved' : 'human:approved',
|
|
651
|
+
...(automaticApproval ? {} : { handledCommentId: latestHumanCommentId(candidate) }),
|
|
652
|
+
...(automaticApproval
|
|
653
|
+
? {
|
|
654
|
+
autoResolution: {
|
|
655
|
+
kind: 'awaiting-approval',
|
|
656
|
+
classification: 'auto-approval',
|
|
657
|
+
reasoning: approvalResolution.reason,
|
|
658
|
+
},
|
|
659
|
+
}
|
|
660
|
+
: {}),
|
|
651
661
|
},
|
|
652
662
|
});
|
|
653
663
|
await deps.stateStore.appendEventEnvelope(approvalCompletedEvent);
|
|
@@ -1001,6 +1011,7 @@ export function createTickRunner(deps) {
|
|
|
1001
1011
|
payload: {
|
|
1002
1012
|
action,
|
|
1003
1013
|
sentinel,
|
|
1014
|
+
allowAutoApproval: runnerResult.metadata?.allowAutoApproval === true,
|
|
1004
1015
|
...(rawSentinel !== sentinel ? { rawSentinel } : {}),
|
|
1005
1016
|
...(nextStage !== null ? { nextStage } : {}),
|
|
1006
1017
|
runId,
|
package/dist/src/version.js
CHANGED
package/package.json
CHANGED