@bpmsoftwaresolutions/ai-engine-client 1.1.16 → 1.1.18

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +68 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpmsoftwaresolutions/ai-engine-client",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "Thin npm client for the AI Engine operator and retrieval APIs",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
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.18';
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,6 +1198,12 @@ export class AIEngineClient {
1164
1198
  }
1165
1199
 
1166
1200
  async updateImplementationItemStatus(implementationItemId, body) {
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
+ });
1167
1207
  const result = await this._request(`/api/governed-implementation/items/${implementationItemId}/status`, {
1168
1208
  method: 'PATCH', body,
1169
1209
  });
@@ -1217,6 +1257,12 @@ export class AIEngineClient {
1217
1257
  }
1218
1258
 
1219
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
+ });
1220
1266
  return this._request(
1221
1267
  `/api/governed-implementation/items/${implementationItemId}/acceptance-checks/${acceptanceCheckId}/status`,
1222
1268
  { method: 'PATCH', body }
@@ -2035,6 +2081,8 @@ export class AIEngineClient {
2035
2081
  const resolvedHeaders = {
2036
2082
  accept: accept || 'application/json',
2037
2083
  'x-actor-id': this.actorId,
2084
+ 'x-ai-engine-client-version': this.clientVersion,
2085
+ 'x-ai-engine-client-capabilities': this.clientCapabilities.join(','),
2038
2086
  ...(isJsonBody(body) ? { 'content-type': 'application/json' } : {}),
2039
2087
  ...(token ? { authorization: `Bearer ${token}` } : {}),
2040
2088
  ...(!token && this.clientId ? { 'x-client-id': this.clientId } : {}),
@@ -2047,6 +2095,26 @@ export class AIEngineClient {
2047
2095
  return resolvedHeaders;
2048
2096
  }
2049
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
+ const remediation = cleanText(eligibility?.remediation_command);
2104
+ const verification = cleanText(eligibility?.verification_command);
2105
+ throw buildEligibilityError(
2106
+ [
2107
+ `Execution eligibility gate blocked mutation${cleanText(body?.requested_mutation_surface) ? ` on ${cleanText(body.requested_mutation_surface)}` : ''}.`,
2108
+ remediation ? `Fix: ${remediation}` : null,
2109
+ verification ? `Then verify: ${verification}` : null,
2110
+ ].filter(Boolean).join(' '),
2111
+ {
2112
+ required_gate: eligibility?.required_gate || 'execution_eligibility.required_gate',
2113
+ execution_eligibility: eligibility ?? null,
2114
+ },
2115
+ );
2116
+ }
2117
+
2050
2118
  async _request(path, { method = 'GET', query, headers, body } = {}) {
2051
2119
  const url = appendQuery(`${this.baseUrl}${path}`, query);
2052
2120
  const controller = new AbortController();