@atolis-hq/wake 0.3.7 → 0.3.8
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/bootstrap/version.js +1 -1
- package/dist/src/integrations/github/application/inbound-review-signals.js +17 -13
- package/dist/src/orchestration/application/advance-workflow.js +28 -1
- package/dist/src/orchestration/application/orchestration-service.js +3 -0
- package/dist/src/orchestration/domain/interpreter.js +1 -1
- package/dist/src/orchestration/domain/operator-retry-policy.js +33 -1
- package/package.json +1 -1
|
@@ -101,6 +101,7 @@ async function applyIssueApprovalSignal(input) {
|
|
|
101
101
|
orchestration,
|
|
102
102
|
resourceId: resourceIdValue,
|
|
103
103
|
outcome: command === '/approved' ? ActivityOutcomeKind.Done : ActivityOutcomeKind.Rejected,
|
|
104
|
+
resumeBlockedOnChanges: command === '/changes',
|
|
104
105
|
});
|
|
105
106
|
}
|
|
106
107
|
async function applyPullRequestWorkflowSignal(input) {
|
|
@@ -125,25 +126,28 @@ async function applyPullRequestWorkflowSignal(input) {
|
|
|
125
126
|
});
|
|
126
127
|
}
|
|
127
128
|
async function applyWorkflowSignal(input) {
|
|
128
|
-
const { event, resources, orchestration, resourceId: resourceIdValue, outcome } = input;
|
|
129
|
+
const { event, resources, orchestration, resourceId: resourceIdValue, outcome, resumeBlockedOnChanges = false, } = input;
|
|
129
130
|
const workItemIds = (await resources.correlations(resourceIdValue))
|
|
130
131
|
.filter((correlation) => correlation.role === ResourceCorrelationRole.Primary)
|
|
131
132
|
.map((correlation) => correlation.workItemId);
|
|
132
133
|
for (const workflow of await orchestration.listAll()) {
|
|
133
134
|
if (!workItemIds.includes(workflow.workItemId))
|
|
134
135
|
continue;
|
|
135
|
-
if (workflow.waitingFor
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
136
|
+
if (workflow.waitingFor !== undefined) {
|
|
137
|
+
await orchestration.acceptSignal(workflow.workflowInstanceId, {
|
|
138
|
+
kind: workflow.waitingFor.signalKind,
|
|
139
|
+
outcome,
|
|
140
|
+
actorId: event.payload.actor.id,
|
|
141
|
+
actorDecision: {
|
|
142
|
+
authorized: event.payload.actor.kind === ReviewActorKind.Human,
|
|
143
|
+
evidenceId: event.eventId,
|
|
144
|
+
},
|
|
145
|
+
providerEventId: event.eventId,
|
|
146
|
+
}, commandContext(event));
|
|
147
|
+
}
|
|
148
|
+
else if (resumeBlockedOnChanges) {
|
|
149
|
+
await orchestration.resumeBlockedStageForChanges(workflow.workflowInstanceId, commandContext(event));
|
|
150
|
+
}
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
function isWakeDelivery(body) {
|
|
@@ -4,7 +4,7 @@ import { OrchestrationEventType } from '../contracts/events.js';
|
|
|
4
4
|
import { commandName, workflowInstanceId, } from '../contracts/identifiers.js';
|
|
5
5
|
import { workflowInstanceStream } from '../contracts/streams.js';
|
|
6
6
|
import { WorkflowStatus } from '../contracts/vocabulary.js';
|
|
7
|
-
import { requestOperatorRetry as decideOperatorRetry, requestSupplementalActivity as decideSupplementalActivity, } from '../domain/interpreter.js';
|
|
7
|
+
import { requestChangesResume as decideChangesResume, requestOperatorRetry as decideOperatorRetry, requestSupplementalActivity as decideSupplementalActivity, } from '../domain/interpreter.js';
|
|
8
8
|
import { isAuthorisedActor } from '../domain/supplemental-policy.js';
|
|
9
9
|
export class OperatorRetryIneligibleError extends Error {
|
|
10
10
|
constructor(detail) {
|
|
@@ -136,6 +136,33 @@ export class AdvanceWorkflow {
|
|
|
136
136
|
}
|
|
137
137
|
return (await this.repository.loadRequired(id)).view;
|
|
138
138
|
}
|
|
139
|
+
async resumeBlockedStageForChanges(id, context) {
|
|
140
|
+
const loaded = await this.repository.load(id);
|
|
141
|
+
if (loaded.view === null)
|
|
142
|
+
return null;
|
|
143
|
+
if (loaded.view.operatorRetryCommandIds.includes(context.commandId))
|
|
144
|
+
return loaded.view;
|
|
145
|
+
const definition = await this.workflows.definitionForOperation(loaded.view, loaded.sequence, context);
|
|
146
|
+
if (definition === null)
|
|
147
|
+
return (await this.repository.loadRequired(id)).view;
|
|
148
|
+
const decision = decideChangesResume(definition, loaded.view, {
|
|
149
|
+
commandId: context.commandId,
|
|
150
|
+
occurredAt: context.occurredAt,
|
|
151
|
+
causationId: context.commandId,
|
|
152
|
+
});
|
|
153
|
+
if (decision.kind === 'ignored')
|
|
154
|
+
return loaded.view;
|
|
155
|
+
try {
|
|
156
|
+
await this.repository.append(id, loaded.sequence, decision.events);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
const reloaded = await this.repository.loadRequired(id);
|
|
160
|
+
if (reloaded.view.operatorRetryCommandIds.includes(context.commandId))
|
|
161
|
+
return reloaded.view;
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
return (await this.repository.loadRequired(id)).view;
|
|
165
|
+
}
|
|
139
166
|
async get(id) {
|
|
140
167
|
return (await this.repository.load(id)).view;
|
|
141
168
|
}
|
|
@@ -55,6 +55,9 @@ export class OrchestrationService {
|
|
|
55
55
|
retryBlockedFailedStage(workflowInstanceId, context) {
|
|
56
56
|
return this.advanceWorkflow.retryBlockedFailedStage(workflowInstanceId, context);
|
|
57
57
|
}
|
|
58
|
+
resumeBlockedStageForChanges(workflowInstanceId, context) {
|
|
59
|
+
return this.advanceWorkflow.resumeBlockedStageForChanges(workflowInstanceId, context);
|
|
60
|
+
}
|
|
58
61
|
get(id) {
|
|
59
62
|
return this.advanceWorkflow.get(id);
|
|
60
63
|
}
|
|
@@ -10,7 +10,7 @@ import { finishRoute } from './transition.js';
|
|
|
10
10
|
export { startInstance } from './activation-policy.js';
|
|
11
11
|
export { acceptSignal, waitForSignal } from './signal-policy.js';
|
|
12
12
|
export { requestSupplementalActivity } from './supplemental-policy.js';
|
|
13
|
-
export { isOperatorRetryEligible, requestOperatorRetry } from './operator-retry-policy.js';
|
|
13
|
+
export { isChangesResumeEligible, isOperatorRetryEligible, requestChangesResume, requestOperatorRetry, } from './operator-retry-policy.js';
|
|
14
14
|
export function acceptActivityOutcome(definition, state, input) {
|
|
15
15
|
if (!isPendingOutcome(state, input))
|
|
16
16
|
return { kind: 'ignored', reason: 'outcome is not for the pending activation' };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActivityOutcomeKind } from '../../activities/index.js';
|
|
1
|
+
import { ActivityOutcomeKind, BuiltInActivityName } from '../../activities/index.js';
|
|
2
2
|
import { OrchestrationEventType } from '../contracts/events.js';
|
|
3
3
|
import { stageName } from '../contracts/identifiers.js';
|
|
4
4
|
import { ActivityActivationStatus, WorkflowStatus } from '../contracts/vocabulary.js';
|
|
@@ -14,6 +14,18 @@ export function isOperatorRetryEligible(view) {
|
|
|
14
14
|
view.lastOutcome?.kind === ActivityOutcomeKind.Failed &&
|
|
15
15
|
view.acceptedOutcomes.includes(pending.activationId));
|
|
16
16
|
}
|
|
17
|
+
export function isChangesResumeEligible(view) {
|
|
18
|
+
const pending = view.pendingActivation;
|
|
19
|
+
return (view.status === WorkflowStatus.Blocked &&
|
|
20
|
+
view.blockReason === 'unconfigured outcome blocked' &&
|
|
21
|
+
pending !== undefined &&
|
|
22
|
+
pending.activity === BuiltInActivityName.Agent &&
|
|
23
|
+
pending.status === ActivityActivationStatus.Completed &&
|
|
24
|
+
pending.supplemental !== true &&
|
|
25
|
+
pending.followOnIndex === undefined &&
|
|
26
|
+
view.lastOutcome?.kind === ActivityOutcomeKind.Blocked &&
|
|
27
|
+
view.acceptedOutcomes.includes(pending.activationId));
|
|
28
|
+
}
|
|
17
29
|
export function requestOperatorRetry(definition, state, input) {
|
|
18
30
|
if (input.commandId.length === 0)
|
|
19
31
|
return { kind: 'ignored', reason: 'operator retry command id is required' };
|
|
@@ -32,3 +44,23 @@ export function requestOperatorRetry(definition, state, input) {
|
|
|
32
44
|
];
|
|
33
45
|
return { kind: 'append', events };
|
|
34
46
|
}
|
|
47
|
+
export function requestChangesResume(definition, state, input) {
|
|
48
|
+
if (input.commandId.length === 0)
|
|
49
|
+
return { kind: 'ignored', reason: 'changes resume command id is required' };
|
|
50
|
+
if (!isChangesResumeEligible(state))
|
|
51
|
+
return {
|
|
52
|
+
kind: 'ignored',
|
|
53
|
+
reason: 'workflow is not blocked for an unconfigured agent blocked outcome',
|
|
54
|
+
};
|
|
55
|
+
const stage = definition.stages[stageName(state.currentStage)];
|
|
56
|
+
return {
|
|
57
|
+
kind: 'append',
|
|
58
|
+
events: [
|
|
59
|
+
stateDraft(state, input, OrchestrationEventType.OperatorRetryRequested, { activationId: state.pendingActivation.activationId, commandId: input.commandId }, 1),
|
|
60
|
+
stateDraft(state, input, OrchestrationEventType.ActivityRequested, activation(state.workflowInstanceId, nextOrdinal(state), stage.activity, stage.with, {
|
|
61
|
+
execution: stage.execution,
|
|
62
|
+
stage: stageName(state.currentStage),
|
|
63
|
+
}), 2),
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|