@bpmsoftwaresolutions/ai-engine-client 1.1.29 → 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 +163 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpmsoftwaresolutions/ai-engine-client",
3
- "version": "1.1.29",
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.29';
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',
@@ -113,10 +113,60 @@ function cleanText(value) {
113
113
  return text || null;
114
114
  }
115
115
 
116
+ function cleanList(value) {
117
+ if (!Array.isArray(value)) return [];
118
+ return value.map((item) => cleanText(item)).filter(Boolean);
119
+ }
120
+
116
121
  function isPlainObject(value) {
117
122
  return value !== null && typeof value === 'object' && !Array.isArray(value);
118
123
  }
119
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
+
120
170
  function matchesExpectedState(actual, expected) {
121
171
  if (typeof expected === 'function') {
122
172
  throw new Error('matchesExpectedState does not support function expectations.');
@@ -456,6 +506,115 @@ export class AIEngineClient {
456
506
  return this._request('/api/governance/claims/claim-work-item', { method: 'POST', body });
457
507
  }
458
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
+
459
618
  async getClaim(claimId) {
460
619
  if (!claimId) throw new Error('claimId is required.');
461
620
  return this._request(`/api/governance/claims/${encodeURIComponent(claimId)}`);
@@ -2348,7 +2507,9 @@ export class AIEngineClient {
2348
2507
  });
2349
2508
  }
2350
2509
 
2351
- async lockContextSessionClaim(contextSessionId) {
2510
+ async lockContextSessionClaim(input) {
2511
+ const contextSessionId = contextSessionIdFromInput(input);
2512
+ if (!contextSessionId) throw new Error('contextSessionId is required.');
2352
2513
  return this._request(`/api/context-session/${encodeURIComponent(contextSessionId)}/lock-claim`, {
2353
2514
  method: 'POST',
2354
2515
  });