@bpmsoftwaresolutions/ai-engine-client 1.1.28 → 1.1.30

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 +164 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpmsoftwaresolutions/ai-engine-client",
3
- "version": "1.1.28",
3
+ "version": "1.1.30",
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,5 +1,5 @@
1
1
  const DEFAULT_TIMEOUT_MS = 30000;
2
- export const AI_ENGINE_CLIENT_VERSION = '1.1.27';
2
+ export const AI_ENGINE_CLIENT_VERSION = '1.1.30';
3
3
  export const GOVERNED_MUTATION_REQUIRED_CAPABILITIES = [
4
4
  'executeVerifiedMutation',
5
5
  'post_mutation_verification',
@@ -11,6 +11,7 @@ export const GOVERNED_MUTATION_REQUIRED_CAPABILITIES = [
11
11
  export const AI_ENGINE_CLIENT_CAPABILITIES = [
12
12
  ...GOVERNED_MUTATION_REQUIRED_CAPABILITIES,
13
13
  'execution_eligibility_read',
14
+ 'project_delivery_facade',
14
15
  'workflow_resume_context_read',
15
16
  ];
16
17
 
@@ -112,10 +113,60 @@ function cleanText(value) {
112
113
  return text || null;
113
114
  }
114
115
 
116
+ function cleanList(value) {
117
+ if (!Array.isArray(value)) return [];
118
+ return value.map((item) => cleanText(item)).filter(Boolean);
119
+ }
120
+
115
121
  function isPlainObject(value) {
116
122
  return value !== null && typeof value === 'object' && !Array.isArray(value);
117
123
  }
118
124
 
125
+ function isActiveBinding(binding) {
126
+ return isPlainObject(binding) && (
127
+ binding.is_active === true ||
128
+ binding.is_active === 1 ||
129
+ binding.is_active === 'true' ||
130
+ binding.is_active === 'True'
131
+ );
132
+ }
133
+
134
+ function activeToolKeysFromRegistry(registry) {
135
+ const bindings = Array.isArray(registry?.bindings) ? registry.bindings : [];
136
+ const seen = new Set();
137
+ const keys = [];
138
+ for (const binding of bindings) {
139
+ if (!isActiveBinding(binding)) continue;
140
+ const key = cleanText(binding.tool_key);
141
+ if (!key || seen.has(key)) continue;
142
+ seen.add(key);
143
+ keys.push(key);
144
+ }
145
+ return keys;
146
+ }
147
+
148
+ function reminderTokens(reminders) {
149
+ const tokens = [];
150
+ for (const reminder of Array.isArray(reminders) ? reminders : []) {
151
+ if (!isPlainObject(reminder)) continue;
152
+ for (const value of [reminder.reminder_key, reminder.reminder_id]) {
153
+ const token = cleanText(value);
154
+ if (token && !tokens.includes(token)) tokens.push(token);
155
+ }
156
+ }
157
+ return tokens;
158
+ }
159
+
160
+ function contextSessionIdFromInput(input) {
161
+ if (isPlainObject(input)) {
162
+ return cleanText(input.contextSessionId)
163
+ || cleanText(input.context_session_id)
164
+ || cleanText(input.context_session?.context_session_id)
165
+ || cleanText(input.claim?.context_session_id);
166
+ }
167
+ return cleanText(input);
168
+ }
169
+
119
170
  function matchesExpectedState(actual, expected) {
120
171
  if (typeof expected === 'function') {
121
172
  throw new Error('matchesExpectedState does not support function expectations.');
@@ -455,6 +506,115 @@ export class AIEngineClient {
455
506
  return this._request('/api/governance/claims/claim-work-item', { method: 'POST', body });
456
507
  }
457
508
 
509
+ async bindClaimedWorkItem({
510
+ claimId,
511
+ contextSessionId,
512
+ workflowRunId,
513
+ agentSessionId,
514
+ claimedItemId,
515
+ claimedItemKey,
516
+ claimName,
517
+ actorId,
518
+ workflowId,
519
+ workflowSlug,
520
+ requiredToolKeys,
521
+ declaredScopeFiles,
522
+ allowedMutationSurfaces,
523
+ successCriteria,
524
+ acknowledgedReminders,
525
+ intentConfirmation,
526
+ actorSignature,
527
+ autoSignoff = true,
528
+ metadata,
529
+ } = {}) {
530
+ const normalizedContextSessionId = contextSessionIdFromInput({
531
+ contextSessionId,
532
+ context_session_id: contextSessionId,
533
+ claim: { context_session_id: contextSessionId },
534
+ });
535
+ const normalizedClaimId = cleanText(claimId);
536
+ const normalizedWorkflowRunId = cleanText(workflowRunId);
537
+ const normalizedAgentSessionId = cleanText(agentSessionId);
538
+ const normalizedClaimedItemId = cleanText(claimedItemId);
539
+ const normalizedClaimedItemKey = cleanText(claimedItemKey);
540
+ const normalizedClaimedItemRef = normalizedClaimedItemId || normalizedClaimedItemKey;
541
+ if (!normalizedContextSessionId) throw new Error('contextSessionId is required.');
542
+ if (!normalizedWorkflowRunId) throw new Error('workflowRunId is required.');
543
+ if (!normalizedAgentSessionId) throw new Error('agentSessionId is required.');
544
+ if (!normalizedClaimedItemRef) throw new Error('claimedItemId or claimedItemKey is required.');
545
+
546
+ const normalizedWorkflowId = cleanText(workflowId);
547
+ const normalizedWorkflowSlug = cleanText(workflowSlug);
548
+ let normalizedRequiredToolKeys = cleanList(requiredToolKeys);
549
+ let workflowRegistry = null;
550
+ if (normalizedRequiredToolKeys.length === 0 && (normalizedWorkflowId || normalizedWorkflowSlug)) {
551
+ workflowRegistry = await this.getWorkflowToolRegistry({
552
+ workflowId: normalizedWorkflowId,
553
+ workflowSlug: normalizedWorkflowSlug,
554
+ });
555
+ normalizedRequiredToolKeys = activeToolKeysFromRegistry(workflowRegistry);
556
+ }
557
+
558
+ const lockClaimResult = await this.lockContextSessionClaim(normalizedContextSessionId);
559
+ const lockedContextSession = lockClaimResult?.context_session || lockClaimResult;
560
+ const lockedContextSessionId = contextSessionIdFromInput(lockedContextSession) || normalizedContextSessionId;
561
+
562
+ const claimWorkItemResult = await this.claimWorkItem({
563
+ context_session_id: lockedContextSessionId,
564
+ workflow_run_id: normalizedWorkflowRunId,
565
+ agent_session_id: normalizedAgentSessionId,
566
+ claimed_item_id: normalizedClaimedItemId,
567
+ claimed_item_key: normalizedClaimedItemKey,
568
+ claim_name: claimName,
569
+ actor_id: actorId,
570
+ workflow_id: normalizedWorkflowId,
571
+ workflow_slug: normalizedWorkflowSlug,
572
+ required_tool_keys: normalizedRequiredToolKeys,
573
+ declared_scope_files: cleanList(declaredScopeFiles).length > 0
574
+ ? cleanList(declaredScopeFiles)
575
+ : [normalizedClaimedItemRef],
576
+ allowed_mutation_surfaces: cleanList(allowedMutationSurfaces).length > 0
577
+ ? cleanList(allowedMutationSurfaces)
578
+ : ['project_roadmap_task'],
579
+ success_criteria: successCriteria,
580
+ metadata,
581
+ });
582
+
583
+ let signedClaim = null;
584
+ const resolvedClaimId = cleanText(claimWorkItemResult?.claim_id) || normalizedClaimId;
585
+ if (autoSignoff && resolvedClaimId) {
586
+ const claimEnvelope = isPlainObject(claimWorkItemResult?.claim) ? claimWorkItemResult.claim : claimWorkItemResult;
587
+ const reminders = cleanList(acknowledgedReminders).length > 0
588
+ ? cleanList(acknowledgedReminders)
589
+ : reminderTokens(claimEnvelope?.required_reminders);
590
+ if (reminders.length > 0) {
591
+ signedClaim = await this.signoffClaim(resolvedClaimId, {
592
+ acknowledged_reminders: reminders,
593
+ intent_confirmation: cleanText(intentConfirmation) || 'I confirm this governed execution intent and scope.',
594
+ actor_signature: cleanText(actorSignature) || cleanText(actorId) || this.actorId,
595
+ });
596
+ }
597
+ }
598
+
599
+ return {
600
+ claim_id: resolvedClaimId,
601
+ context_session_id: lockedContextSessionId,
602
+ workflow_run_id: normalizedWorkflowRunId,
603
+ agent_session_id: normalizedAgentSessionId,
604
+ required_tool_keys: normalizedRequiredToolKeys,
605
+ workflow_registry: workflowRegistry,
606
+ lock_claim_result: lockClaimResult,
607
+ claim_work_item_result: claimWorkItemResult,
608
+ signed_claim: signedClaim,
609
+ approved_tool_keys: cleanList(
610
+ signedClaim?.approved_tool_keys ||
611
+ signedClaim?.claim?.approved_tool_keys ||
612
+ claimWorkItemResult?.approved_tool_keys ||
613
+ claimWorkItemResult?.claim?.approved_tool_keys
614
+ ),
615
+ };
616
+ }
617
+
458
618
  async getClaim(claimId) {
459
619
  if (!claimId) throw new Error('claimId is required.');
460
620
  return this._request(`/api/governance/claims/${encodeURIComponent(claimId)}`);
@@ -2347,7 +2507,9 @@ export class AIEngineClient {
2347
2507
  });
2348
2508
  }
2349
2509
 
2350
- async lockContextSessionClaim(contextSessionId) {
2510
+ async lockContextSessionClaim(input) {
2511
+ const contextSessionId = contextSessionIdFromInput(input);
2512
+ if (!contextSessionId) throw new Error('contextSessionId is required.');
2351
2513
  return this._request(`/api/context-session/${encodeURIComponent(contextSessionId)}/lock-claim`, {
2352
2514
  method: 'POST',
2353
2515
  });