@bpmsoftwaresolutions/ai-engine-client 1.1.15 → 1.1.17
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/package.json +1 -1
- package/src/index.js +101 -6
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
const DEFAULT_TIMEOUT_MS = 30000;
|
|
2
|
+
export const AI_ENGINE_CLIENT_VERSION = '1.1.17';
|
|
3
|
+
export const GOVERNED_MUTATION_REQUIRED_CAPABILITIES = [
|
|
4
|
+
'executeVerifiedMutation',
|
|
5
|
+
'post_mutation_verification',
|
|
6
|
+
'acceptance_check_read',
|
|
7
|
+
'artifact_manifest_read',
|
|
8
|
+
'decision_packet_read',
|
|
9
|
+
'claim_signoff',
|
|
10
|
+
];
|
|
11
|
+
export const AI_ENGINE_CLIENT_CAPABILITIES = [
|
|
12
|
+
...GOVERNED_MUTATION_REQUIRED_CAPABILITIES,
|
|
13
|
+
'execution_eligibility_read',
|
|
14
|
+
'workflow_resume_context_read',
|
|
15
|
+
];
|
|
2
16
|
|
|
3
17
|
export const LOGA_CONTRACT = 'ai-engine-ui/v1';
|
|
4
18
|
export const LOGA_INTERACTION_CONTRACT = 'loga-choreography/v1';
|
|
@@ -124,6 +138,13 @@ function buildVerificationError(message, details = {}) {
|
|
|
124
138
|
return error;
|
|
125
139
|
}
|
|
126
140
|
|
|
141
|
+
function buildEligibilityError(message, details = {}) {
|
|
142
|
+
const error = new Error(message);
|
|
143
|
+
error.code = 'EXECUTION_ELIGIBILITY_FAILED';
|
|
144
|
+
error.details = details;
|
|
145
|
+
return error;
|
|
146
|
+
}
|
|
147
|
+
|
|
127
148
|
export class AIEngineClient {
|
|
128
149
|
constructor({ baseUrl, accessToken, tokenProvider, apiKey, clientId, actorId, fetchImpl, timeoutMs } = {}) {
|
|
129
150
|
if (!baseUrl) throw new Error('baseUrl is required.');
|
|
@@ -135,6 +156,8 @@ export class AIEngineClient {
|
|
|
135
156
|
this.actorId = actorId || 'sdk:npm-ai-engine-client';
|
|
136
157
|
this.fetchImpl = fetchImpl || globalThis.fetch;
|
|
137
158
|
this.timeoutMs = timeoutMs || DEFAULT_TIMEOUT_MS;
|
|
159
|
+
this.clientVersion = AI_ENGINE_CLIENT_VERSION;
|
|
160
|
+
this.clientCapabilities = [...AI_ENGINE_CLIENT_CAPABILITIES];
|
|
138
161
|
if (typeof this.fetchImpl !== 'function') {
|
|
139
162
|
throw new Error('A fetch implementation is required. Use Node 18+ or supply fetchImpl.');
|
|
140
163
|
}
|
|
@@ -399,6 +422,17 @@ export class AIEngineClient {
|
|
|
399
422
|
return this._request('/api/governance/claims/claim-work-item', { method: 'POST', body });
|
|
400
423
|
}
|
|
401
424
|
|
|
425
|
+
async getExecutionEligibility(body = {}) {
|
|
426
|
+
return this._request('/api/governance/execution-eligibility', {
|
|
427
|
+
method: 'POST',
|
|
428
|
+
body: {
|
|
429
|
+
required_capabilities: GOVERNED_MUTATION_REQUIRED_CAPABILITIES,
|
|
430
|
+
verification_required: true,
|
|
431
|
+
...body,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
402
436
|
async signoffClaim(claimId, body = {}) {
|
|
403
437
|
if (!claimId) throw new Error('claimId is required.');
|
|
404
438
|
return this._request(`/api/governance/claims/${encodeURIComponent(claimId)}/signoff`, {
|
|
@@ -1164,9 +1198,46 @@ export class AIEngineClient {
|
|
|
1164
1198
|
}
|
|
1165
1199
|
|
|
1166
1200
|
async updateImplementationItemStatus(implementationItemId, body) {
|
|
1167
|
-
|
|
1201
|
+
await this._assertExecutionEligibility({
|
|
1202
|
+
...(body || {}),
|
|
1203
|
+
claimed_item_id: body?.claimed_item_id || implementationItemId,
|
|
1204
|
+
requested_mutation_surface: body?.requested_mutation_surface || 'implementation_packet_item',
|
|
1205
|
+
verification_required: true,
|
|
1206
|
+
});
|
|
1207
|
+
const result = await this._request(`/api/governed-implementation/items/${implementationItemId}/status`, {
|
|
1168
1208
|
method: 'PATCH', body,
|
|
1169
1209
|
});
|
|
1210
|
+
const expectedStatus = cleanText(body?.status);
|
|
1211
|
+
const authoritativeItem = isPlainObject(result?.implementation_item) ? result.implementation_item : null;
|
|
1212
|
+
const actualStatus = cleanText(authoritativeItem?.status);
|
|
1213
|
+
if (expectedStatus && !authoritativeItem) {
|
|
1214
|
+
throw buildVerificationError(
|
|
1215
|
+
`updateImplementationItemStatus did not return authoritative item state for ${implementationItemId}.`,
|
|
1216
|
+
{
|
|
1217
|
+
mutation_name: 'updateImplementationItemStatus',
|
|
1218
|
+
mutation_attempted: true,
|
|
1219
|
+
authoritative_read_performed: false,
|
|
1220
|
+
post_condition_verified: false,
|
|
1221
|
+
expected_state: { status: expectedStatus },
|
|
1222
|
+
mutation_result: result ?? null,
|
|
1223
|
+
},
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
if (expectedStatus && actualStatus !== expectedStatus) {
|
|
1227
|
+
throw buildVerificationError(
|
|
1228
|
+
`updateImplementationItemStatus returned ${actualStatus ?? 'unknown'} instead of ${expectedStatus}.`,
|
|
1229
|
+
{
|
|
1230
|
+
mutation_name: 'updateImplementationItemStatus',
|
|
1231
|
+
mutation_attempted: true,
|
|
1232
|
+
authoritative_read_performed: true,
|
|
1233
|
+
post_condition_verified: false,
|
|
1234
|
+
expected_state: { status: expectedStatus },
|
|
1235
|
+
verified_current_state: authoritativeItem ?? result ?? null,
|
|
1236
|
+
mutation_result: result ?? null,
|
|
1237
|
+
},
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
return result;
|
|
1170
1241
|
}
|
|
1171
1242
|
|
|
1172
1243
|
async addImplementationItemEvidence(implementationItemId, body) {
|
|
@@ -1186,6 +1257,12 @@ export class AIEngineClient {
|
|
|
1186
1257
|
}
|
|
1187
1258
|
|
|
1188
1259
|
async updateAcceptanceCheckStatus(implementationItemId, acceptanceCheckId, body) {
|
|
1260
|
+
await this._assertExecutionEligibility({
|
|
1261
|
+
...(body || {}),
|
|
1262
|
+
claimed_item_id: body?.claimed_item_id || implementationItemId,
|
|
1263
|
+
requested_mutation_surface: body?.requested_mutation_surface || 'implementation_acceptance_check',
|
|
1264
|
+
verification_required: true,
|
|
1265
|
+
});
|
|
1189
1266
|
return this._request(
|
|
1190
1267
|
`/api/governed-implementation/items/${implementationItemId}/acceptance-checks/${acceptanceCheckId}/status`,
|
|
1191
1268
|
{ method: 'PATCH', body }
|
|
@@ -1316,15 +1393,17 @@ export class AIEngineClient {
|
|
|
1316
1393
|
}
|
|
1317
1394
|
|
|
1318
1395
|
async updateImplementationItemStatusVerified(implementationItemId, status, body = {}) {
|
|
1319
|
-
const workflowId = cleanText(body.workflowId) || cleanText(body.workflow_id);
|
|
1320
|
-
if (!workflowId) {
|
|
1321
|
-
throw new Error('workflowId is required to verify implementation item status.');
|
|
1322
|
-
}
|
|
1323
1396
|
return this.executeVerifiedMutation({
|
|
1324
1397
|
mutationName: 'updateImplementationItemStatus',
|
|
1325
1398
|
evidenceLabel: `implementation-item-status:${implementationItemId}`,
|
|
1326
1399
|
mutationFn: () => this.updateImplementationItemStatus(implementationItemId, { ...body, status }),
|
|
1327
|
-
verificationFn: async () => {
|
|
1400
|
+
verificationFn: async (mutationResult) => {
|
|
1401
|
+
const workflowId = cleanText(body.workflowId)
|
|
1402
|
+
|| cleanText(body.workflow_id)
|
|
1403
|
+
|| cleanText(mutationResult?.workflow_id);
|
|
1404
|
+
if (!workflowId) {
|
|
1405
|
+
throw new Error('workflowId is required to verify implementation item status.');
|
|
1406
|
+
}
|
|
1328
1407
|
const roadmap = await this.getWorkflowImplementationRoadmap(workflowId);
|
|
1329
1408
|
const items = Array.isArray(roadmap?.items) ? roadmap.items : [];
|
|
1330
1409
|
const matchedItem = items.find((item) => item?.implementation_item_id === implementationItemId) || null;
|
|
@@ -2002,6 +2081,8 @@ export class AIEngineClient {
|
|
|
2002
2081
|
const resolvedHeaders = {
|
|
2003
2082
|
accept: accept || 'application/json',
|
|
2004
2083
|
'x-actor-id': this.actorId,
|
|
2084
|
+
'x-ai-engine-client-version': this.clientVersion,
|
|
2085
|
+
'x-ai-engine-client-capabilities': this.clientCapabilities.join(','),
|
|
2005
2086
|
...(isJsonBody(body) ? { 'content-type': 'application/json' } : {}),
|
|
2006
2087
|
...(token ? { authorization: `Bearer ${token}` } : {}),
|
|
2007
2088
|
...(!token && this.clientId ? { 'x-client-id': this.clientId } : {}),
|
|
@@ -2014,6 +2095,20 @@ export class AIEngineClient {
|
|
|
2014
2095
|
return resolvedHeaders;
|
|
2015
2096
|
}
|
|
2016
2097
|
|
|
2098
|
+
async _assertExecutionEligibility(body = {}) {
|
|
2099
|
+
const eligibility = await this.getExecutionEligibility(body);
|
|
2100
|
+
if (eligibility?.execution_eligible && eligibility?.mutation_allowed) {
|
|
2101
|
+
return eligibility;
|
|
2102
|
+
}
|
|
2103
|
+
throw buildEligibilityError(
|
|
2104
|
+
`Execution eligibility gate blocked mutation${cleanText(body?.requested_mutation_surface) ? ` on ${cleanText(body.requested_mutation_surface)}` : ''}.`,
|
|
2105
|
+
{
|
|
2106
|
+
required_gate: eligibility?.required_gate || 'execution_eligibility.required_gate',
|
|
2107
|
+
execution_eligibility: eligibility ?? null,
|
|
2108
|
+
},
|
|
2109
|
+
);
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2017
2112
|
async _request(path, { method = 'GET', query, headers, body } = {}) {
|
|
2018
2113
|
const url = appendQuery(`${this.baseUrl}${path}`, query);
|
|
2019
2114
|
const controller = new AbortController();
|