@cadenza.io/service 2.4.0 → 2.6.0

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/index.js CHANGED
@@ -295,7 +295,74 @@ var DatabaseTask = class extends DeputyTask {
295
295
  var isNode = typeof process !== "undefined" && process.versions?.node != null;
296
296
  var isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
297
297
 
298
+ // src/utils/inquiry.ts
299
+ var META_INTENT_PREFIX = "meta-";
300
+ var META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT = "meta-runtime-transport-diagnostics";
301
+ function isPlainObject(value) {
302
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
303
+ }
304
+ function deepMergeDeterministic(left, right) {
305
+ if (Array.isArray(left) && Array.isArray(right)) {
306
+ return [...left, ...right];
307
+ }
308
+ if (isPlainObject(left) && isPlainObject(right)) {
309
+ const merged = { ...left };
310
+ const keys = Array.from(/* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)])).sort();
311
+ for (const key of keys) {
312
+ if (!(key in left)) {
313
+ merged[key] = right[key];
314
+ continue;
315
+ }
316
+ if (!(key in right)) {
317
+ merged[key] = left[key];
318
+ continue;
319
+ }
320
+ merged[key] = deepMergeDeterministic(left[key], right[key]);
321
+ }
322
+ return merged;
323
+ }
324
+ return right;
325
+ }
326
+ function mergeInquiryContexts(contexts) {
327
+ return contexts.reduce((acc, next) => deepMergeDeterministic(acc, next), {});
328
+ }
329
+ function isMetaIntentName(intentName) {
330
+ return intentName.startsWith(META_INTENT_PREFIX);
331
+ }
332
+ function shouldExecuteInquiryResponder(inquiry, responderIsMeta) {
333
+ if (!isMetaIntentName(inquiry)) {
334
+ return true;
335
+ }
336
+ return responderIsMeta;
337
+ }
338
+ function compareResponderDescriptors(left, right) {
339
+ if (left.serviceName !== right.serviceName) {
340
+ return left.serviceName.localeCompare(right.serviceName);
341
+ }
342
+ if (left.taskName !== right.taskName) {
343
+ return left.taskName.localeCompare(right.taskName);
344
+ }
345
+ if (left.taskVersion !== right.taskVersion) {
346
+ return left.taskVersion - right.taskVersion;
347
+ }
348
+ return left.localTaskName.localeCompare(right.localTaskName);
349
+ }
350
+ function summarizeResponderStatuses(statuses) {
351
+ let responded = 0;
352
+ let failed = 0;
353
+ let timedOut = 0;
354
+ let pending = 0;
355
+ for (const status of statuses) {
356
+ if (status.status === "fulfilled") responded++;
357
+ if (status.status === "failed") failed++;
358
+ if (status.status === "timed_out") timedOut++;
359
+ }
360
+ pending = Math.max(0, statuses.length - responded - failed - timedOut);
361
+ return { responded, failed, timedOut, pending };
362
+ }
363
+
298
364
  // src/registry/ServiceRegistry.ts
365
+ var META_SERVICE_REGISTRY_FULL_SYNC_INTENT = "meta-service-registry-full-sync";
299
366
  var ServiceRegistry = class _ServiceRegistry {
300
367
  /**
301
368
  * Initializes a private constructor for managing service instances, remote signals,
@@ -310,11 +377,47 @@ var ServiceRegistry = class _ServiceRegistry {
310
377
  this.instances = /* @__PURE__ */ new Map();
311
378
  this.deputies = /* @__PURE__ */ new Map();
312
379
  this.remoteSignals = /* @__PURE__ */ new Map();
380
+ this.remoteIntents = /* @__PURE__ */ new Map();
381
+ this.remoteIntentDeputiesByKey = /* @__PURE__ */ new Map();
382
+ this.remoteIntentDeputiesByTask = /* @__PURE__ */ new Map();
313
383
  this.serviceName = null;
314
384
  this.serviceInstanceId = null;
315
385
  this.numberOfRunningGraphs = 0;
316
386
  this.useSocket = false;
317
387
  this.retryCount = 3;
388
+ CadenzaService.defineIntent({
389
+ name: META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT,
390
+ description: "Gather transport diagnostics across all services and communication clients.",
391
+ input: {
392
+ type: "object",
393
+ properties: {
394
+ detailLevel: {
395
+ type: "string",
396
+ constraints: {
397
+ oneOf: ["summary", "full"]
398
+ }
399
+ },
400
+ includeErrorHistory: {
401
+ type: "boolean"
402
+ },
403
+ errorHistoryLimit: {
404
+ type: "number",
405
+ constraints: {
406
+ min: 1,
407
+ max: 200
408
+ }
409
+ }
410
+ }
411
+ },
412
+ output: {
413
+ type: "object",
414
+ properties: {
415
+ transportDiagnostics: {
416
+ type: "object"
417
+ }
418
+ }
419
+ }
420
+ });
318
421
  this.handleInstanceUpdateTask = CadenzaService.createMetaTask(
319
422
  "Handle Instance Update",
320
423
  (ctx, emit) => {
@@ -354,7 +457,7 @@ var ServiceRegistry = class _ServiceRegistry {
354
457
  if (this.serviceName === serviceName) {
355
458
  return false;
356
459
  }
357
- if (!isFrontend && this.deputies.has(serviceName) || this.remoteSignals.has(serviceName)) {
460
+ if (!isFrontend && (this.deputies.has(serviceName) || this.remoteIntents.has(serviceName)) || this.remoteSignals.has(serviceName)) {
358
461
  const clientCreated = instances?.some(
359
462
  (i) => i.address === address && i.port === port && i.clientCreated && i.isActive
360
463
  );
@@ -404,7 +507,10 @@ var ServiceRegistry = class _ServiceRegistry {
404
507
  for (const serviceInstance of ctx.serviceInstances) {
405
508
  yield { serviceInstance };
406
509
  }
407
- }).doOn("meta.service_registry.registered_global_signals").then(this.handleInstanceUpdateTask);
510
+ }).doOn(
511
+ "meta.service_registry.registered_global_signals",
512
+ "meta.service_registry.registered_global_intents"
513
+ ).then(this.handleInstanceUpdateTask);
408
514
  this.handleGlobalSignalRegistrationTask = CadenzaService.createMetaTask(
409
515
  "Handle global Signal Registration",
410
516
  (ctx) => {
@@ -445,6 +551,32 @@ var ServiceRegistry = class _ServiceRegistry {
445
551
  },
446
552
  "Handles registration of remote signals"
447
553
  ).emits("meta.service_registry.registered_global_signals").doOn("global.meta.cadenza_db.gathered_sync_data");
554
+ this.handleGlobalIntentRegistrationTask = CadenzaService.createMetaTask(
555
+ "Handle global intent registration",
556
+ (ctx) => {
557
+ const intentToTaskMaps = this.normalizeIntentMaps(ctx);
558
+ const sorted = intentToTaskMaps.sort((a, b) => {
559
+ if (a.deleted && !b.deleted) return -1;
560
+ if (!a.deleted && b.deleted) return 1;
561
+ return 0;
562
+ });
563
+ for (const map of sorted) {
564
+ if (map.deleted) {
565
+ this.unregisterRemoteIntentDeputy(map);
566
+ continue;
567
+ }
568
+ CadenzaService.inquiryBroker.addIntent({
569
+ name: map.intentName
570
+ });
571
+ this.registerRemoteIntentDeputy(map);
572
+ }
573
+ return true;
574
+ },
575
+ "Handles registration of remote inquiry intent responders"
576
+ ).emits("meta.service_registry.registered_global_intents").doOn(
577
+ "global.meta.cadenza_db.gathered_sync_data",
578
+ "global.meta.graph_metadata.task_intent_associated"
579
+ );
448
580
  this.handleServiceNotRespondingTask = CadenzaService.createMetaTask(
449
581
  "Handle service not responding",
450
582
  (ctx, emit) => {
@@ -549,45 +681,58 @@ var ServiceRegistry = class _ServiceRegistry {
549
681
  },
550
682
  "Handles status update from socket broadcast"
551
683
  ).doOn("meta.socket_client.status_received");
552
- const mergeSyncDataTask = CadenzaService.createUniqueMetaTask(
553
- "Merge sync data",
554
- (ctx) => {
555
- let joinedContext = {};
556
- ctx.joinedContexts.forEach((ctx2) => {
557
- joinedContext = { ...joinedContext, ...ctx2 };
558
- });
559
- return joinedContext;
560
- }
561
- ).emits("meta.service_registry.initial_sync_complete").then(this.handleGlobalSignalRegistrationTask);
562
- this.fullSyncTask = CadenzaService.createMetaRoutine("Full sync", [
563
- CadenzaService.createCadenzaDBQueryTask("signal_to_task_map", {
564
- filter: {
565
- isGlobal: true
566
- },
567
- fields: ["signal_name", "service_name", "deleted"]
568
- }).then(mergeSyncDataTask),
569
- CadenzaService.createCadenzaDBQueryTask("service_instance", {
570
- filter: {
571
- deleted: false,
572
- is_active: true,
573
- is_non_responsive: false,
574
- is_blocked: false
575
- },
576
- fields: [
577
- "uuid",
578
- "address",
579
- "port",
580
- "service_name",
581
- "is_active",
582
- "is_non_responsive",
583
- "is_blocked",
584
- "health",
585
- "exposed",
586
- "created",
587
- "is_frontend"
588
- ]
589
- }).then(mergeSyncDataTask)
590
- ]).doOn("meta.sync_requested");
684
+ this.fullSyncTask = CadenzaService.createMetaTask(
685
+ "Full sync",
686
+ async (ctx) => {
687
+ const inquiryResult = await CadenzaService.inquire(
688
+ META_SERVICE_REGISTRY_FULL_SYNC_INTENT,
689
+ {
690
+ syncScope: "service-registry-full-sync"
691
+ },
692
+ ctx.inquiryOptions ?? ctx.__inquiryOptions ?? {}
693
+ );
694
+ const signalToTaskMaps = (inquiryResult.signalToTaskMaps ?? []).filter((m) => !!m.isGlobal).map((m) => ({
695
+ signalName: m.signalName,
696
+ serviceName: m.serviceName,
697
+ deleted: !!m.deleted
698
+ }));
699
+ const intentToTaskMaps = (inquiryResult.intentToTaskMaps ?? []).map(
700
+ (m) => ({
701
+ intentName: m.intentName,
702
+ taskName: m.taskName,
703
+ taskVersion: m.taskVersion ?? 1,
704
+ serviceName: m.serviceName,
705
+ deleted: !!m.deleted
706
+ })
707
+ );
708
+ const serviceInstances = (inquiryResult.serviceInstances ?? []).filter(
709
+ (instance) => !instance.deleted && !!instance.isActive && !instance.isNonResponsive && !instance.isBlocked
710
+ ).map((instance) => ({
711
+ uuid: instance.uuid,
712
+ address: instance.address,
713
+ port: instance.port,
714
+ serviceName: instance.serviceName,
715
+ isActive: !!instance.isActive,
716
+ isNonResponsive: !!instance.isNonResponsive,
717
+ isBlocked: !!instance.isBlocked,
718
+ health: instance.health ?? {},
719
+ exposed: !!instance.exposed,
720
+ created: instance.created,
721
+ isFrontend: !!instance.isFrontend
722
+ }));
723
+ return {
724
+ ...ctx,
725
+ signalToTaskMaps,
726
+ intentToTaskMaps,
727
+ serviceInstances,
728
+ __inquiryMeta: inquiryResult.__inquiryMeta
729
+ };
730
+ },
731
+ "Runs service registry full sync through one distributed inquiry intent."
732
+ ).doOn("meta.sync_requested").emits("meta.service_registry.initial_sync_complete").then(
733
+ this.handleGlobalSignalRegistrationTask,
734
+ this.handleGlobalIntentRegistrationTask
735
+ );
591
736
  this.getInstanceById = CadenzaService.createMetaTask(
592
737
  "Get instance by id",
593
738
  (context) => {
@@ -756,6 +901,25 @@ var ServiceRegistry = class _ServiceRegistry {
756
901
  __active: self?.isActive ?? false
757
902
  };
758
903
  }).doOn("meta.socket.status_check_requested");
904
+ this.collectTransportDiagnosticsTask = CadenzaService.createMetaTask(
905
+ "Collect transport diagnostics",
906
+ async (ctx) => {
907
+ const inquiryResult = await CadenzaService.inquire(
908
+ META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT,
909
+ {
910
+ detailLevel: ctx.detailLevel,
911
+ includeErrorHistory: ctx.includeErrorHistory,
912
+ errorHistoryLimit: ctx.errorHistoryLimit
913
+ },
914
+ ctx.inquiryOptions ?? ctx.__inquiryOptions ?? {}
915
+ );
916
+ return {
917
+ ...ctx,
918
+ ...inquiryResult
919
+ };
920
+ },
921
+ "Collects distributed transport diagnostics using inquiry responders."
922
+ ).doOn("meta.service_registry.transport_diagnostics_requested").emits("meta.service_registry.transport_diagnostics_collected").emitsOnFail("meta.service_registry.transport_diagnostics_failed");
759
923
  this.insertServiceTask = CadenzaService.createCadenzaDBInsertTask(
760
924
  "service",
761
925
  {
@@ -964,8 +1128,125 @@ var ServiceRegistry = class _ServiceRegistry {
964
1128
  if (!this._instance) this._instance = new _ServiceRegistry();
965
1129
  return this._instance;
966
1130
  }
1131
+ buildRemoteIntentDeputyKey(map) {
1132
+ return `${map.intentName}|${map.serviceName}|${map.taskName}|${map.taskVersion ?? 1}`;
1133
+ }
1134
+ normalizeIntentMaps(ctx) {
1135
+ if (Array.isArray(ctx.intentToTaskMaps)) {
1136
+ return ctx.intentToTaskMaps.map((m) => ({
1137
+ intentName: m.intentName ?? m.intent_name,
1138
+ serviceName: m.serviceName ?? m.service_name,
1139
+ taskName: m.taskName ?? m.task_name,
1140
+ taskVersion: m.taskVersion ?? m.task_version ?? 1,
1141
+ deleted: !!m.deleted
1142
+ })).filter((m) => m.intentName && m.serviceName && m.taskName);
1143
+ }
1144
+ const single = ctx.intentToTaskMap ?? ctx.data ?? (ctx.intentName ? ctx : void 0);
1145
+ if (!single) return [];
1146
+ const normalized = {
1147
+ intentName: single.intentName ?? single.intent_name,
1148
+ serviceName: single.serviceName ?? single.service_name,
1149
+ taskName: single.taskName ?? single.task_name,
1150
+ taskVersion: single.taskVersion ?? single.task_version ?? 1,
1151
+ deleted: !!single.deleted
1152
+ };
1153
+ if (!normalized.intentName || !normalized.serviceName || !normalized.taskName)
1154
+ return [];
1155
+ return [normalized];
1156
+ }
1157
+ registerRemoteIntentDeputy(map) {
1158
+ if (!this.serviceName || map.serviceName === this.serviceName) {
1159
+ return;
1160
+ }
1161
+ const key = this.buildRemoteIntentDeputyKey(map);
1162
+ if (this.remoteIntentDeputiesByKey.has(key)) {
1163
+ return;
1164
+ }
1165
+ const deputyTaskName = `Inquire ${map.intentName} via ${map.serviceName} (${map.taskName} v${map.taskVersion})`;
1166
+ const deputyTask = isMetaIntentName(map.intentName) ? CadenzaService.createMetaDeputyTask(map.taskName, map.serviceName, {
1167
+ register: false,
1168
+ isHidden: true,
1169
+ retryCount: 1,
1170
+ retryDelay: 50,
1171
+ retryDelayFactor: 1.2
1172
+ }) : CadenzaService.createDeputyTask(map.taskName, map.serviceName, {
1173
+ register: false,
1174
+ isHidden: true,
1175
+ retryCount: 1,
1176
+ retryDelay: 50,
1177
+ retryDelayFactor: 1.2
1178
+ });
1179
+ deputyTask.respondsTo(map.intentName);
1180
+ if (!this.remoteIntents.has(map.serviceName)) {
1181
+ this.remoteIntents.set(map.serviceName, /* @__PURE__ */ new Set());
1182
+ }
1183
+ this.remoteIntents.get(map.serviceName).add(map.intentName);
1184
+ const descriptor = {
1185
+ key,
1186
+ intentName: map.intentName,
1187
+ serviceName: map.serviceName,
1188
+ remoteTaskName: map.taskName,
1189
+ remoteTaskVersion: map.taskVersion,
1190
+ localTaskName: deputyTask.name || deputyTaskName,
1191
+ localTask: deputyTask
1192
+ };
1193
+ this.remoteIntentDeputiesByKey.set(key, descriptor);
1194
+ this.remoteIntentDeputiesByTask.set(deputyTask, descriptor);
1195
+ }
1196
+ unregisterRemoteIntentDeputy(map) {
1197
+ const key = this.buildRemoteIntentDeputyKey(map);
1198
+ const descriptor = this.remoteIntentDeputiesByKey.get(key);
1199
+ if (!descriptor) {
1200
+ return;
1201
+ }
1202
+ const task = descriptor.localTask;
1203
+ if (task) {
1204
+ CadenzaService.inquiryBroker.unsubscribe(descriptor.intentName, task);
1205
+ task.destroy();
1206
+ }
1207
+ this.remoteIntentDeputiesByTask.delete(descriptor.localTask);
1208
+ this.remoteIntentDeputiesByKey.delete(key);
1209
+ this.remoteIntents.get(descriptor.serviceName)?.delete(descriptor.intentName);
1210
+ if (!this.remoteIntents.get(descriptor.serviceName)?.size) {
1211
+ this.remoteIntents.delete(descriptor.serviceName);
1212
+ }
1213
+ const deputies = this.deputies.get(descriptor.serviceName);
1214
+ if (deputies) {
1215
+ this.deputies.set(
1216
+ descriptor.serviceName,
1217
+ deputies.filter((d) => d.localTaskName !== descriptor.localTaskName)
1218
+ );
1219
+ if (this.deputies.get(descriptor.serviceName)?.length === 0) {
1220
+ this.deputies.delete(descriptor.serviceName);
1221
+ }
1222
+ }
1223
+ }
1224
+ getInquiryResponderDescriptor(task) {
1225
+ const remote = this.remoteIntentDeputiesByTask.get(task);
1226
+ if (remote) {
1227
+ return {
1228
+ isRemote: true,
1229
+ serviceName: remote.serviceName,
1230
+ taskName: remote.remoteTaskName,
1231
+ taskVersion: remote.remoteTaskVersion,
1232
+ localTaskName: remote.localTaskName
1233
+ };
1234
+ }
1235
+ return {
1236
+ isRemote: false,
1237
+ serviceName: this.serviceName ?? "UnknownService",
1238
+ taskName: task.name,
1239
+ taskVersion: task.version,
1240
+ localTaskName: task.name
1241
+ };
1242
+ }
967
1243
  reset() {
968
1244
  this.instances.clear();
1245
+ this.deputies.clear();
1246
+ this.remoteSignals.clear();
1247
+ this.remoteIntents.clear();
1248
+ this.remoteIntentDeputiesByKey.clear();
1249
+ this.remoteIntentDeputiesByTask.clear();
969
1250
  }
970
1251
  };
971
1252
 
@@ -1082,6 +1363,8 @@ var RestController = class _RestController {
1082
1363
  * It initializes and configures the REST server tasks.
1083
1364
  */
1084
1365
  constructor() {
1366
+ this.fetchClientDiagnostics = /* @__PURE__ */ new Map();
1367
+ this.diagnosticsErrorHistoryLimit = 100;
1085
1368
  /**
1086
1369
  * Fetches data from the given URL with a specified timeout. This function performs
1087
1370
  * a fetch request with the ability to cancel the request if it exceeds the provided timeout duration.
@@ -1122,6 +1405,11 @@ var RestController = class _RestController {
1122
1405
  "meta.rest.delegation_requested",
1123
1406
  "meta.socket.delegation_requested"
1124
1407
  );
1408
+ CadenzaService.createMetaTask(
1409
+ "Collect fetch transport diagnostics",
1410
+ (ctx) => this.collectFetchTransportDiagnostics(ctx),
1411
+ "Responds to distributed transport diagnostics inquiries with REST/fetch client data."
1412
+ ).respondsTo(META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT);
1125
1413
  CadenzaService.createMetaRoutine(
1126
1414
  "RestServer",
1127
1415
  [
@@ -1452,6 +1740,13 @@ var RestController = class _RestController {
1452
1740
  const port = protocol === "https" ? 443 : servicePort;
1453
1741
  const URL = `${protocol}://${serviceAddress}:${port}`;
1454
1742
  const fetchId = `${serviceAddress}_${port}`;
1743
+ const fetchDiagnostics = this.ensureFetchClientDiagnostics(
1744
+ fetchId,
1745
+ serviceName,
1746
+ URL
1747
+ );
1748
+ fetchDiagnostics.destroyed = false;
1749
+ fetchDiagnostics.updatedAt = Date.now();
1455
1750
  if (CadenzaService.get(`Send Handshake to ${URL}`)) {
1456
1751
  console.error("Fetch client already exists", URL);
1457
1752
  return;
@@ -1473,6 +1768,10 @@ var RestController = class _RestController {
1473
1768
  );
1474
1769
  if (response.__status !== "success") {
1475
1770
  const error = response.__error ?? `Failed to connect to service ${serviceName} ${ctx2.serviceInstanceId}`;
1771
+ fetchDiagnostics.connected = false;
1772
+ fetchDiagnostics.lastHandshakeError = error;
1773
+ fetchDiagnostics.updatedAt = Date.now();
1774
+ this.recordFetchClientError(fetchId, serviceName, URL, error);
1476
1775
  CadenzaService.log(
1477
1776
  "Fetch handshake failed.",
1478
1777
  { error, serviceName, URL },
@@ -1482,6 +1781,11 @@ var RestController = class _RestController {
1482
1781
  return { ...ctx2, __error: error, errored: true };
1483
1782
  }
1484
1783
  ctx2.serviceInstanceId = response.__serviceInstanceId;
1784
+ fetchDiagnostics.connected = true;
1785
+ fetchDiagnostics.destroyed = false;
1786
+ fetchDiagnostics.lastHandshakeAt = (/* @__PURE__ */ new Date()).toISOString();
1787
+ fetchDiagnostics.lastHandshakeError = null;
1788
+ fetchDiagnostics.updatedAt = Date.now();
1485
1789
  CadenzaService.log("Fetch client connected.", {
1486
1790
  response,
1487
1791
  serviceName,
@@ -1497,6 +1801,10 @@ var RestController = class _RestController {
1497
1801
  });
1498
1802
  }
1499
1803
  } catch (e) {
1804
+ fetchDiagnostics.connected = false;
1805
+ fetchDiagnostics.lastHandshakeError = this.getErrorMessage(e);
1806
+ fetchDiagnostics.updatedAt = Date.now();
1807
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1500
1808
  CadenzaService.log(
1501
1809
  "Error in fetch handshake",
1502
1810
  { error: e, serviceName, URL, ctx: ctx2 },
@@ -1518,6 +1826,8 @@ var RestController = class _RestController {
1518
1826
  if (ctx2.__remoteRoutineName === void 0) {
1519
1827
  return;
1520
1828
  }
1829
+ fetchDiagnostics.delegationRequests++;
1830
+ fetchDiagnostics.updatedAt = Date.now();
1521
1831
  let resultContext;
1522
1832
  try {
1523
1833
  resultContext = await this.fetchDataWithTimeout(
@@ -1531,8 +1841,21 @@ var RestController = class _RestController {
1531
1841
  },
1532
1842
  3e4
1533
1843
  );
1844
+ if (resultContext?.errored || resultContext?.failed) {
1845
+ fetchDiagnostics.delegationFailures++;
1846
+ fetchDiagnostics.updatedAt = Date.now();
1847
+ this.recordFetchClientError(
1848
+ fetchId,
1849
+ serviceName,
1850
+ URL,
1851
+ resultContext?.__error ?? resultContext?.error ?? "Delegation failed"
1852
+ );
1853
+ }
1534
1854
  } catch (e) {
1535
1855
  console.error("Error in delegation", e);
1856
+ fetchDiagnostics.delegationFailures++;
1857
+ fetchDiagnostics.updatedAt = Date.now();
1858
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1536
1859
  resultContext = {
1537
1860
  __error: `Error: ${e}`,
1538
1861
  errored: true,
@@ -1558,6 +1881,8 @@ var RestController = class _RestController {
1558
1881
  if (ctx2.__signalName === void 0) {
1559
1882
  return;
1560
1883
  }
1884
+ fetchDiagnostics.signalTransmissions++;
1885
+ fetchDiagnostics.updatedAt = Date.now();
1561
1886
  let response;
1562
1887
  try {
1563
1888
  response = await this.fetchDataWithTimeout(
@@ -1574,8 +1899,21 @@ var RestController = class _RestController {
1574
1899
  if (ctx2.__routineExecId) {
1575
1900
  emit(`meta.fetch.transmitted:${ctx2.__routineExecId}`, response);
1576
1901
  }
1902
+ if (response?.errored || response?.failed) {
1903
+ fetchDiagnostics.signalFailures++;
1904
+ fetchDiagnostics.updatedAt = Date.now();
1905
+ this.recordFetchClientError(
1906
+ fetchId,
1907
+ serviceName,
1908
+ URL,
1909
+ response?.__error ?? response?.error ?? "Signal transmission failed"
1910
+ );
1911
+ }
1577
1912
  } catch (e) {
1578
1913
  console.error("Error in transmission", e);
1914
+ fetchDiagnostics.signalFailures++;
1915
+ fetchDiagnostics.updatedAt = Date.now();
1916
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1579
1917
  response = {
1580
1918
  __error: `Error: ${e}`,
1581
1919
  errored: true,
@@ -1593,6 +1931,8 @@ var RestController = class _RestController {
1593
1931
  const statusTask = CadenzaService.createMetaTask(
1594
1932
  `Request status from ${URL}`,
1595
1933
  async (ctx2) => {
1934
+ fetchDiagnostics.statusChecks++;
1935
+ fetchDiagnostics.updatedAt = Date.now();
1596
1936
  let status;
1597
1937
  try {
1598
1938
  status = await this.fetchDataWithTimeout(
@@ -1602,7 +1942,20 @@ var RestController = class _RestController {
1602
1942
  },
1603
1943
  1e3
1604
1944
  );
1945
+ if (status?.errored || status?.failed) {
1946
+ fetchDiagnostics.statusFailures++;
1947
+ fetchDiagnostics.updatedAt = Date.now();
1948
+ this.recordFetchClientError(
1949
+ fetchId,
1950
+ serviceName,
1951
+ URL,
1952
+ status?.__error ?? status?.error ?? "Status check failed"
1953
+ );
1954
+ }
1605
1955
  } catch (e) {
1956
+ fetchDiagnostics.statusFailures++;
1957
+ fetchDiagnostics.updatedAt = Date.now();
1958
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1606
1959
  status = {
1607
1960
  __error: `Error: ${e}`,
1608
1961
  errored: true,
@@ -1614,6 +1967,9 @@ var RestController = class _RestController {
1614
1967
  "Requests status"
1615
1968
  ).doOn("meta.fetch.status_check_requested").emits("meta.fetch.status_checked").emitsOnFail("meta.fetch.status_check_failed");
1616
1969
  CadenzaService.createEphemeralMetaTask("Destroy fetch client", () => {
1970
+ fetchDiagnostics.connected = false;
1971
+ fetchDiagnostics.destroyed = true;
1972
+ fetchDiagnostics.updatedAt = Date.now();
1617
1973
  CadenzaService.log("Destroying fetch client", { URL, serviceName });
1618
1974
  handshakeTask.destroy();
1619
1975
  delegateTask.destroy();
@@ -1662,6 +2018,144 @@ var RestController = class _RestController {
1662
2018
  if (!this._instance) this._instance = new _RestController();
1663
2019
  return this._instance;
1664
2020
  }
2021
+ resolveTransportDiagnosticsOptions(ctx) {
2022
+ const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
2023
+ const includeErrorHistory = Boolean(ctx.includeErrorHistory);
2024
+ const requestedLimit = Number(ctx.errorHistoryLimit);
2025
+ const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
2026
+ return {
2027
+ detailLevel,
2028
+ includeErrorHistory,
2029
+ errorHistoryLimit
2030
+ };
2031
+ }
2032
+ ensureFetchClientDiagnostics(fetchId, serviceName, url) {
2033
+ let state = this.fetchClientDiagnostics.get(fetchId);
2034
+ if (!state) {
2035
+ state = {
2036
+ fetchId,
2037
+ serviceName,
2038
+ url,
2039
+ connected: false,
2040
+ destroyed: false,
2041
+ lastHandshakeAt: null,
2042
+ lastHandshakeError: null,
2043
+ lastError: null,
2044
+ lastErrorAt: 0,
2045
+ errorHistory: [],
2046
+ delegationRequests: 0,
2047
+ delegationFailures: 0,
2048
+ signalTransmissions: 0,
2049
+ signalFailures: 0,
2050
+ statusChecks: 0,
2051
+ statusFailures: 0,
2052
+ updatedAt: Date.now()
2053
+ };
2054
+ this.fetchClientDiagnostics.set(fetchId, state);
2055
+ } else {
2056
+ state.serviceName = serviceName;
2057
+ state.url = url;
2058
+ }
2059
+ return state;
2060
+ }
2061
+ getErrorMessage(error) {
2062
+ if (error instanceof Error) {
2063
+ return error.message;
2064
+ }
2065
+ if (typeof error === "string") {
2066
+ return error;
2067
+ }
2068
+ try {
2069
+ return JSON.stringify(error);
2070
+ } catch {
2071
+ return String(error);
2072
+ }
2073
+ }
2074
+ recordFetchClientError(fetchId, serviceName, url, error) {
2075
+ const state = this.ensureFetchClientDiagnostics(fetchId, serviceName, url);
2076
+ const message = this.getErrorMessage(error);
2077
+ const now = Date.now();
2078
+ state.lastError = message;
2079
+ state.lastErrorAt = now;
2080
+ state.updatedAt = now;
2081
+ state.errorHistory.push({ at: new Date(now).toISOString(), message });
2082
+ if (state.errorHistory.length > this.diagnosticsErrorHistoryLimit) {
2083
+ state.errorHistory.splice(
2084
+ 0,
2085
+ state.errorHistory.length - this.diagnosticsErrorHistoryLimit
2086
+ );
2087
+ }
2088
+ }
2089
+ collectFetchTransportDiagnostics(ctx) {
2090
+ const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
2091
+ const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
2092
+ const states = Array.from(this.fetchClientDiagnostics.values()).sort(
2093
+ (a, b) => a.fetchId.localeCompare(b.fetchId)
2094
+ );
2095
+ const summary = {
2096
+ detailLevel,
2097
+ totalClients: states.length,
2098
+ connectedClients: states.filter((state) => state.connected).length,
2099
+ destroyedClients: states.filter((state) => state.destroyed).length,
2100
+ delegationRequests: states.reduce(
2101
+ (acc, state) => acc + state.delegationRequests,
2102
+ 0
2103
+ ),
2104
+ delegationFailures: states.reduce(
2105
+ (acc, state) => acc + state.delegationFailures,
2106
+ 0
2107
+ ),
2108
+ signalTransmissions: states.reduce(
2109
+ (acc, state) => acc + state.signalTransmissions,
2110
+ 0
2111
+ ),
2112
+ signalFailures: states.reduce((acc, state) => acc + state.signalFailures, 0),
2113
+ statusChecks: states.reduce((acc, state) => acc + state.statusChecks, 0),
2114
+ statusFailures: states.reduce((acc, state) => acc + state.statusFailures, 0),
2115
+ latestError: states.slice().sort((a, b) => b.lastErrorAt - a.lastErrorAt).find((state) => state.lastError)?.lastError ?? null
2116
+ };
2117
+ if (detailLevel === "summary") {
2118
+ return {
2119
+ transportDiagnostics: {
2120
+ [serviceName]: {
2121
+ fetchClient: summary
2122
+ }
2123
+ }
2124
+ };
2125
+ }
2126
+ const clients = states.map((state) => {
2127
+ const details = {
2128
+ fetchId: state.fetchId,
2129
+ serviceName: state.serviceName,
2130
+ url: state.url,
2131
+ connected: state.connected,
2132
+ destroyed: state.destroyed,
2133
+ lastHandshakeAt: state.lastHandshakeAt,
2134
+ lastHandshakeError: state.lastHandshakeError,
2135
+ latestError: state.lastError,
2136
+ delegationRequests: state.delegationRequests,
2137
+ delegationFailures: state.delegationFailures,
2138
+ signalTransmissions: state.signalTransmissions,
2139
+ signalFailures: state.signalFailures,
2140
+ statusChecks: state.statusChecks,
2141
+ statusFailures: state.statusFailures
2142
+ };
2143
+ if (includeErrorHistory) {
2144
+ details.errorHistory = state.errorHistory.slice(-errorHistoryLimit);
2145
+ }
2146
+ return details;
2147
+ });
2148
+ return {
2149
+ transportDiagnostics: {
2150
+ [serviceName]: {
2151
+ fetchClient: {
2152
+ ...summary,
2153
+ clients
2154
+ }
2155
+ }
2156
+ }
2157
+ };
2158
+ }
1665
2159
  };
1666
2160
 
1667
2161
  // src/network/SocketController.ts
@@ -1709,10 +2203,6 @@ var waitForSocketConnection = async (socket, timeoutMs, createError) => {
1709
2203
 
1710
2204
  // src/network/SocketController.ts
1711
2205
  var SocketController = class _SocketController {
1712
- static get instance() {
1713
- if (!this._instance) this._instance = new _SocketController();
1714
- return this._instance;
1715
- }
1716
2206
  /**
1717
2207
  * Constructs the `SocketServer`, setting up a WebSocket server with specific configurations,
1718
2208
  * including connection state recovery, rate limiting, CORS handling, and custom event handling.
@@ -1731,6 +2221,13 @@ var SocketController = class _SocketController {
1731
2221
  * Initializes the `SocketServer` to be ready for WebSocket communication.
1732
2222
  */
1733
2223
  constructor() {
2224
+ this.socketClientDiagnostics = /* @__PURE__ */ new Map();
2225
+ this.diagnosticsErrorHistoryLimit = 100;
2226
+ CadenzaService.createMetaTask(
2227
+ "Collect socket transport diagnostics",
2228
+ (ctx) => this.collectSocketTransportDiagnostics(ctx),
2229
+ "Responds to distributed transport diagnostics inquiries with socket client data."
2230
+ ).respondsTo(META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT);
1734
2231
  CadenzaService.createMetaRoutine(
1735
2232
  "SocketServer",
1736
2233
  [
@@ -1958,6 +2455,13 @@ var SocketController = class _SocketController {
1958
2455
  const port = protocol === "https" ? 443 : servicePort;
1959
2456
  const URL = `${socketProtocol}://${serviceAddress}:${port}`;
1960
2457
  const fetchId = `${serviceAddress}_${port}`;
2458
+ const socketDiagnostics = this.ensureSocketClientDiagnostics(
2459
+ fetchId,
2460
+ serviceName,
2461
+ URL
2462
+ );
2463
+ socketDiagnostics.destroyed = false;
2464
+ socketDiagnostics.updatedAt = Date.now();
1961
2465
  let handshake = false;
1962
2466
  let errorCount = 0;
1963
2467
  const ERROR_LIMIT = 5;
@@ -1967,6 +2471,11 @@ var SocketController = class _SocketController {
1967
2471
  }
1968
2472
  const pendingDelegationIds = /* @__PURE__ */ new Set();
1969
2473
  const pendingTimers = /* @__PURE__ */ new Set();
2474
+ const syncPendingCounts = () => {
2475
+ socketDiagnostics.pendingDelegations = pendingDelegationIds.size;
2476
+ socketDiagnostics.pendingTimers = pendingTimers.size;
2477
+ socketDiagnostics.updatedAt = Date.now();
2478
+ };
1970
2479
  let handshakeTask = null;
1971
2480
  let emitWhenReady = null;
1972
2481
  let transmitTask = null;
@@ -2016,6 +2525,12 @@ var SocketController = class _SocketController {
2016
2525
  { socketId: socket?.id, serviceName, URL, event },
2017
2526
  "error"
2018
2527
  );
2528
+ this.recordSocketClientError(
2529
+ fetchId,
2530
+ serviceName,
2531
+ URL,
2532
+ waitResult.error
2533
+ );
2019
2534
  resolveWithError(waitResult.error);
2020
2535
  return;
2021
2536
  }
@@ -2024,6 +2539,7 @@ var SocketController = class _SocketController {
2024
2539
  timer = setTimeout(() => {
2025
2540
  if (timer) {
2026
2541
  pendingTimers.delete(timer);
2542
+ syncPendingCounts();
2027
2543
  timer = null;
2028
2544
  }
2029
2545
  CadenzaService.log(
@@ -2031,9 +2547,16 @@ var SocketController = class _SocketController {
2031
2547
  { socketId: socket?.id, serviceName, URL },
2032
2548
  "error"
2033
2549
  );
2550
+ this.recordSocketClientError(
2551
+ fetchId,
2552
+ serviceName,
2553
+ URL,
2554
+ `Socket event '${event}' timed out`
2555
+ );
2034
2556
  resolveWithError(`Socket event '${event}' timed out`);
2035
2557
  }, timeoutMs + 10);
2036
2558
  pendingTimers.add(timer);
2559
+ syncPendingCounts();
2037
2560
  }
2038
2561
  const connectedSocket = socket;
2039
2562
  if (!connectedSocket) {
@@ -2046,6 +2569,7 @@ var SocketController = class _SocketController {
2046
2569
  if (timer) {
2047
2570
  clearTimeout(timer);
2048
2571
  pendingTimers.delete(timer);
2572
+ syncPendingCounts();
2049
2573
  timer = null;
2050
2574
  }
2051
2575
  if (err) {
@@ -2059,6 +2583,12 @@ var SocketController = class _SocketController {
2059
2583
  },
2060
2584
  "warning"
2061
2585
  );
2586
+ this.recordSocketClientError(
2587
+ fetchId,
2588
+ serviceName,
2589
+ URL,
2590
+ err
2591
+ );
2062
2592
  response = {
2063
2593
  __error: `Timeout error: ${err}`,
2064
2594
  errored: true,
@@ -2075,6 +2605,10 @@ var SocketController = class _SocketController {
2075
2605
  };
2076
2606
  socket.on("connect", () => {
2077
2607
  if (handshake) return;
2608
+ socketDiagnostics.connected = true;
2609
+ socketDiagnostics.destroyed = false;
2610
+ socketDiagnostics.socketId = socket?.id ?? null;
2611
+ socketDiagnostics.updatedAt = Date.now();
2078
2612
  CadenzaService.emit(`meta.socket_client.connected:${fetchId}`, ctx);
2079
2613
  });
2080
2614
  socket.on("delegation_progress", (ctx2) => {
@@ -2093,6 +2627,12 @@ var SocketController = class _SocketController {
2093
2627
  });
2094
2628
  socket.on("connect_error", (err) => {
2095
2629
  handshake = false;
2630
+ socketDiagnostics.connected = false;
2631
+ socketDiagnostics.handshake = false;
2632
+ socketDiagnostics.connectErrors++;
2633
+ socketDiagnostics.lastHandshakeError = err.message;
2634
+ socketDiagnostics.updatedAt = Date.now();
2635
+ this.recordSocketClientError(fetchId, serviceName, URL, err);
2096
2636
  CadenzaService.log(
2097
2637
  "Socket connect error",
2098
2638
  { error: err.message, serviceName, socketId: socket?.id, URL },
@@ -2101,9 +2641,16 @@ var SocketController = class _SocketController {
2101
2641
  CadenzaService.emit(`meta.socket_client.connect_error:${fetchId}`, err);
2102
2642
  });
2103
2643
  socket.on("reconnect_attempt", (attempt) => {
2644
+ socketDiagnostics.reconnectAttempts = Math.max(
2645
+ socketDiagnostics.reconnectAttempts,
2646
+ attempt
2647
+ );
2648
+ socketDiagnostics.updatedAt = Date.now();
2104
2649
  CadenzaService.log(`Reconnect attempt: ${attempt}`);
2105
2650
  });
2106
2651
  socket.on("reconnect", (attempt) => {
2652
+ socketDiagnostics.connected = true;
2653
+ socketDiagnostics.updatedAt = Date.now();
2107
2654
  CadenzaService.log(`Socket reconnected after ${attempt} tries`, {
2108
2655
  socketId: socket?.id,
2109
2656
  URL,
@@ -2112,6 +2659,12 @@ var SocketController = class _SocketController {
2112
2659
  });
2113
2660
  socket.on("reconnect_error", (err) => {
2114
2661
  handshake = false;
2662
+ socketDiagnostics.connected = false;
2663
+ socketDiagnostics.handshake = false;
2664
+ socketDiagnostics.reconnectErrors++;
2665
+ socketDiagnostics.lastHandshakeError = err.message;
2666
+ socketDiagnostics.updatedAt = Date.now();
2667
+ this.recordSocketClientError(fetchId, serviceName, URL, err);
2115
2668
  CadenzaService.log(
2116
2669
  "Socket reconnect failed.",
2117
2670
  { error: err.message, serviceName, URL, socketId: socket?.id },
@@ -2120,6 +2673,9 @@ var SocketController = class _SocketController {
2120
2673
  });
2121
2674
  socket.on("error", (err) => {
2122
2675
  errorCount++;
2676
+ socketDiagnostics.socketErrors++;
2677
+ socketDiagnostics.updatedAt = Date.now();
2678
+ this.recordSocketClientError(fetchId, serviceName, URL, err);
2123
2679
  CadenzaService.log(
2124
2680
  "Socket error",
2125
2681
  { error: err, socketId: socket?.id, URL, serviceName },
@@ -2128,6 +2684,11 @@ var SocketController = class _SocketController {
2128
2684
  CadenzaService.emit("meta.socket_client.error", err);
2129
2685
  });
2130
2686
  socket.on("disconnect", () => {
2687
+ const disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
2688
+ socketDiagnostics.connected = false;
2689
+ socketDiagnostics.handshake = false;
2690
+ socketDiagnostics.lastDisconnectAt = disconnectedAt;
2691
+ socketDiagnostics.updatedAt = Date.now();
2131
2692
  CadenzaService.log(
2132
2693
  "Socket disconnected.",
2133
2694
  { URL, serviceName, socketId: socket?.id },
@@ -2146,6 +2707,8 @@ var SocketController = class _SocketController {
2146
2707
  async (ctx2, emit) => {
2147
2708
  if (handshake) return;
2148
2709
  handshake = true;
2710
+ socketDiagnostics.handshake = true;
2711
+ socketDiagnostics.updatedAt = Date.now();
2149
2712
  await emitWhenReady?.(
2150
2713
  "handshake",
2151
2714
  {
@@ -2157,6 +2720,11 @@ var SocketController = class _SocketController {
2157
2720
  1e4,
2158
2721
  (result) => {
2159
2722
  if (result.status === "success") {
2723
+ socketDiagnostics.connected = true;
2724
+ socketDiagnostics.handshake = true;
2725
+ socketDiagnostics.lastHandshakeAt = (/* @__PURE__ */ new Date()).toISOString();
2726
+ socketDiagnostics.lastHandshakeError = null;
2727
+ socketDiagnostics.updatedAt = Date.now();
2160
2728
  CadenzaService.log("Socket client connected", {
2161
2729
  result,
2162
2730
  serviceName,
@@ -2164,6 +2732,16 @@ var SocketController = class _SocketController {
2164
2732
  URL
2165
2733
  });
2166
2734
  } else {
2735
+ socketDiagnostics.connected = false;
2736
+ socketDiagnostics.handshake = false;
2737
+ socketDiagnostics.lastHandshakeError = result?.__error ?? result?.error ?? "Socket handshake failed";
2738
+ socketDiagnostics.updatedAt = Date.now();
2739
+ this.recordSocketClientError(
2740
+ fetchId,
2741
+ serviceName,
2742
+ URL,
2743
+ socketDiagnostics.lastHandshakeError
2744
+ );
2167
2745
  CadenzaService.log(
2168
2746
  "Socket handshake failed",
2169
2747
  { result, serviceName, socketId: socket?.id, URL },
@@ -2186,6 +2764,7 @@ var SocketController = class _SocketController {
2186
2764
  delete ctx2.__broadcast;
2187
2765
  const requestSentAt = Date.now();
2188
2766
  pendingDelegationIds.add(ctx2.__metadata.__deputyExecId);
2767
+ syncPendingCounts();
2189
2768
  emitWhenReady?.(
2190
2769
  "delegation",
2191
2770
  ctx2,
@@ -2203,6 +2782,15 @@ var SocketController = class _SocketController {
2203
2782
  }
2204
2783
  );
2205
2784
  pendingDelegationIds.delete(ctx2.__metadata.__deputyExecId);
2785
+ syncPendingCounts();
2786
+ if (resultContext?.errored || resultContext?.failed) {
2787
+ this.recordSocketClientError(
2788
+ fetchId,
2789
+ serviceName,
2790
+ URL,
2791
+ resultContext?.__error ?? resultContext?.error ?? "Socket delegation failed"
2792
+ );
2793
+ }
2206
2794
  resolve(resultContext);
2207
2795
  }
2208
2796
  );
@@ -2238,6 +2826,10 @@ var SocketController = class _SocketController {
2238
2826
  `Shutdown SocketClient ${URL}`,
2239
2827
  (ctx2, emit) => {
2240
2828
  handshake = false;
2829
+ socketDiagnostics.connected = false;
2830
+ socketDiagnostics.handshake = false;
2831
+ socketDiagnostics.destroyed = true;
2832
+ socketDiagnostics.updatedAt = Date.now();
2241
2833
  CadenzaService.log("Shutting down socket client", { URL, serviceName });
2242
2834
  socket?.close();
2243
2835
  handshakeTask?.destroy();
@@ -2267,10 +2859,12 @@ var SocketController = class _SocketController {
2267
2859
  });
2268
2860
  }
2269
2861
  pendingDelegationIds.clear();
2862
+ syncPendingCounts();
2270
2863
  for (const timer of pendingTimers) {
2271
2864
  clearTimeout(timer);
2272
2865
  }
2273
2866
  pendingTimers.clear();
2867
+ syncPendingCounts();
2274
2868
  },
2275
2869
  "Shuts down the socket client"
2276
2870
  ).doOn(
@@ -2284,6 +2878,157 @@ var SocketController = class _SocketController {
2284
2878
  "Connects to a specified socket server"
2285
2879
  ).doOn("meta.fetch.handshake_complete").emitsOnFail("meta.socket_client.connect_failed");
2286
2880
  }
2881
+ static get instance() {
2882
+ if (!this._instance) this._instance = new _SocketController();
2883
+ return this._instance;
2884
+ }
2885
+ resolveTransportDiagnosticsOptions(ctx) {
2886
+ const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
2887
+ const includeErrorHistory = Boolean(ctx.includeErrorHistory);
2888
+ const requestedLimit = Number(ctx.errorHistoryLimit);
2889
+ const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
2890
+ return {
2891
+ detailLevel,
2892
+ includeErrorHistory,
2893
+ errorHistoryLimit
2894
+ };
2895
+ }
2896
+ ensureSocketClientDiagnostics(fetchId, serviceName, url) {
2897
+ let state = this.socketClientDiagnostics.get(fetchId);
2898
+ if (!state) {
2899
+ state = {
2900
+ fetchId,
2901
+ serviceName,
2902
+ url,
2903
+ socketId: null,
2904
+ connected: false,
2905
+ handshake: false,
2906
+ reconnectAttempts: 0,
2907
+ connectErrors: 0,
2908
+ reconnectErrors: 0,
2909
+ socketErrors: 0,
2910
+ pendingDelegations: 0,
2911
+ pendingTimers: 0,
2912
+ destroyed: false,
2913
+ lastHandshakeAt: null,
2914
+ lastHandshakeError: null,
2915
+ lastDisconnectAt: null,
2916
+ lastError: null,
2917
+ lastErrorAt: 0,
2918
+ errorHistory: [],
2919
+ updatedAt: Date.now()
2920
+ };
2921
+ this.socketClientDiagnostics.set(fetchId, state);
2922
+ } else {
2923
+ state.serviceName = serviceName;
2924
+ state.url = url;
2925
+ }
2926
+ return state;
2927
+ }
2928
+ getErrorMessage(error) {
2929
+ if (error instanceof Error) {
2930
+ return error.message;
2931
+ }
2932
+ if (typeof error === "string") {
2933
+ return error;
2934
+ }
2935
+ try {
2936
+ return JSON.stringify(error);
2937
+ } catch {
2938
+ return String(error);
2939
+ }
2940
+ }
2941
+ recordSocketClientError(fetchId, serviceName, url, error) {
2942
+ const state = this.ensureSocketClientDiagnostics(fetchId, serviceName, url);
2943
+ const message = this.getErrorMessage(error);
2944
+ const now = Date.now();
2945
+ state.lastError = message;
2946
+ state.lastErrorAt = now;
2947
+ state.updatedAt = now;
2948
+ state.errorHistory.push({
2949
+ at: new Date(now).toISOString(),
2950
+ message
2951
+ });
2952
+ if (state.errorHistory.length > this.diagnosticsErrorHistoryLimit) {
2953
+ state.errorHistory.splice(
2954
+ 0,
2955
+ state.errorHistory.length - this.diagnosticsErrorHistoryLimit
2956
+ );
2957
+ }
2958
+ }
2959
+ collectSocketTransportDiagnostics(ctx) {
2960
+ const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
2961
+ const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
2962
+ const states = Array.from(this.socketClientDiagnostics.values()).sort(
2963
+ (a, b) => a.fetchId.localeCompare(b.fetchId)
2964
+ );
2965
+ const summary = {
2966
+ detailLevel,
2967
+ totalClients: states.length,
2968
+ connectedClients: states.filter((state) => state.connected).length,
2969
+ activeHandshakes: states.filter((state) => state.handshake).length,
2970
+ pendingDelegations: states.reduce(
2971
+ (acc, state) => acc + state.pendingDelegations,
2972
+ 0
2973
+ ),
2974
+ pendingTimers: states.reduce((acc, state) => acc + state.pendingTimers, 0),
2975
+ reconnectAttempts: states.reduce(
2976
+ (acc, state) => acc + state.reconnectAttempts,
2977
+ 0
2978
+ ),
2979
+ connectErrors: states.reduce((acc, state) => acc + state.connectErrors, 0),
2980
+ reconnectErrors: states.reduce(
2981
+ (acc, state) => acc + state.reconnectErrors,
2982
+ 0
2983
+ ),
2984
+ socketErrors: states.reduce((acc, state) => acc + state.socketErrors, 0),
2985
+ latestError: states.slice().sort((a, b) => b.lastErrorAt - a.lastErrorAt).find((state) => state.lastError)?.lastError ?? null
2986
+ };
2987
+ if (detailLevel === "summary") {
2988
+ return {
2989
+ transportDiagnostics: {
2990
+ [serviceName]: {
2991
+ socketClient: summary
2992
+ }
2993
+ }
2994
+ };
2995
+ }
2996
+ const clients = states.map((state) => {
2997
+ const details = {
2998
+ fetchId: state.fetchId,
2999
+ serviceName: state.serviceName,
3000
+ url: state.url,
3001
+ socketId: state.socketId,
3002
+ connected: state.connected,
3003
+ handshake: state.handshake,
3004
+ reconnectAttempts: state.reconnectAttempts,
3005
+ connectErrors: state.connectErrors,
3006
+ reconnectErrors: state.reconnectErrors,
3007
+ socketErrors: state.socketErrors,
3008
+ pendingDelegations: state.pendingDelegations,
3009
+ pendingTimers: state.pendingTimers,
3010
+ destroyed: state.destroyed,
3011
+ lastHandshakeAt: state.lastHandshakeAt,
3012
+ lastHandshakeError: state.lastHandshakeError,
3013
+ lastDisconnectAt: state.lastDisconnectAt,
3014
+ latestError: state.lastError
3015
+ };
3016
+ if (includeErrorHistory) {
3017
+ details.errorHistory = state.errorHistory.slice(-errorHistoryLimit);
3018
+ }
3019
+ return details;
3020
+ });
3021
+ return {
3022
+ transportDiagnostics: {
3023
+ [serviceName]: {
3024
+ socketClient: {
3025
+ ...summary,
3026
+ clients
3027
+ }
3028
+ }
3029
+ }
3030
+ };
3031
+ }
2287
3032
  };
2288
3033
 
2289
3034
  // src/utils/tools.ts
@@ -2387,25 +3132,8 @@ var GraphMetadataController = class _GraphMetadataController {
2387
3132
  return {
2388
3133
  data: {
2389
3134
  ...ctx.data,
2390
- serviceName: CadenzaService.serviceRegistry.serviceName,
2391
- inputContextSchemaId: ctx.data.inputContextSchemaId ? {
2392
- subOperation: "insert",
2393
- table: "context_schema",
2394
- data: {
2395
- ...ctx.data.inputContextSchemaId
2396
- },
2397
- return: "uuid"
2398
- } : null,
2399
- outputContextSchemaId: ctx.data.outputContextSchemaId ? {
2400
- subOperation: "insert",
2401
- table: "context_schema",
2402
- data: {
2403
- ...ctx.data.outputContextSchemaId
2404
- },
2405
- return: "uuid"
2406
- } : null
2407
- },
2408
- transaction: true
3135
+ serviceName: CadenzaService.serviceRegistry.serviceName
3136
+ }
2409
3137
  };
2410
3138
  }).doOn("meta.task.created").emits("global.meta.graph_metadata.task_created");
2411
3139
  CadenzaService.createMetaTask("Handle task update", (ctx) => {
@@ -2612,9 +3340,11 @@ var GraphMetadataController = class _GraphMetadataController {
2612
3340
  { concurrency: 100, isSubMeta: true }
2613
3341
  ).doOn("meta.node.mapped", "meta.node.detected_previous_task_execution").emits("global.meta.graph_metadata.relationship_executed");
2614
3342
  CadenzaService.createMetaTask("Handle Intent Creation", (ctx) => {
3343
+ const intentName = ctx.data?.name;
2615
3344
  return {
2616
3345
  data: {
2617
- ...ctx.data
3346
+ ...ctx.data,
3347
+ isMeta: intentName ? isMetaIntentName(intentName) : false
2618
3348
  }
2619
3349
  };
2620
3350
  }).doOn("meta.inquiry_broker.added").emits("global.meta.graph_metadata.intent_created");
@@ -2646,6 +3376,52 @@ var SCHEMA_TYPES = [
2646
3376
  // src/database/DatabaseController.ts
2647
3377
  var import_pg = require("pg");
2648
3378
  var import_lodash_es = require("lodash-es");
3379
+ function resolveTableQueryIntents(serviceName, tableName, table, defaultInputSchema) {
3380
+ const resolvedServiceName = serviceName ?? "unknown-service";
3381
+ const defaultIntentName = `query-${resolvedServiceName}-${tableName}`;
3382
+ const defaultDescription = `Perform a query operation on the ${tableName} table`;
3383
+ const intents = [
3384
+ {
3385
+ name: defaultIntentName,
3386
+ description: defaultDescription,
3387
+ input: defaultInputSchema
3388
+ }
3389
+ ];
3390
+ const warnings = [];
3391
+ const names = /* @__PURE__ */ new Set([defaultIntentName]);
3392
+ for (const customIntent of table.customIntents?.query ?? []) {
3393
+ const name = typeof customIntent === "string" ? customIntent.trim() : customIntent.intent?.trim();
3394
+ if (!name) {
3395
+ warnings.push(`Skipped empty custom query intent for table '${tableName}'.`);
3396
+ continue;
3397
+ }
3398
+ if (name.length > 100) {
3399
+ warnings.push(
3400
+ `Skipped custom query intent '${name}' for table '${tableName}': name must be <= 100 characters.`
3401
+ );
3402
+ continue;
3403
+ }
3404
+ if (name.includes(" ") || name.includes(".") || name.includes("\\")) {
3405
+ warnings.push(
3406
+ `Skipped custom query intent '${name}' for table '${tableName}': name cannot contain spaces, dots or backslashes.`
3407
+ );
3408
+ continue;
3409
+ }
3410
+ if (names.has(name)) {
3411
+ warnings.push(
3412
+ `Skipped duplicate custom query intent '${name}' for table '${tableName}'.`
3413
+ );
3414
+ continue;
3415
+ }
3416
+ names.add(name);
3417
+ intents.push({
3418
+ name,
3419
+ description: typeof customIntent === "string" ? `Perform a query operation on the ${tableName} table` : customIntent.description ?? defaultDescription,
3420
+ input: typeof customIntent === "string" ? defaultInputSchema : customIntent.input ?? defaultInputSchema
3421
+ });
3422
+ }
3423
+ return { intents, warnings };
3424
+ }
2649
3425
  var DatabaseController = class _DatabaseController {
2650
3426
  /**
2651
3427
  * Constructor for initializing the `DatabaseService` class.
@@ -2787,6 +3563,20 @@ var DatabaseController = class _DatabaseController {
2787
3563
  }
2788
3564
  }
2789
3565
  }
3566
+ if (table.customIntents?.query) {
3567
+ if (!Array.isArray(table.customIntents.query)) {
3568
+ throw new Error(
3569
+ `Invalid customIntents.query for ${tableName}: expected array`
3570
+ );
3571
+ }
3572
+ for (const customIntent of table.customIntents.query) {
3573
+ if (typeof customIntent !== "string" && (typeof customIntent !== "object" || !customIntent || typeof customIntent.intent !== "string")) {
3574
+ throw new Error(
3575
+ `Invalid custom query intent on ${tableName}: expected string or object with intent`
3576
+ );
3577
+ }
3578
+ }
3579
+ }
2790
3580
  }
2791
3581
  }
2792
3582
  console.log("SCHEMA VALIDATED");
@@ -3823,13 +4613,30 @@ var DatabaseController = class _DatabaseController {
3823
4613
  }) ?? []
3824
4614
  );
3825
4615
  if (op === "query") {
3826
- const intentName = `query-${CadenzaService.serviceRegistry.serviceName}-${tableName}`;
3827
- CadenzaService.defineIntent({
3828
- name: intentName,
3829
- description: `Perform a query operation on the ${tableName} table`,
3830
- input: schema
3831
- });
3832
- task.respondsTo(intentName);
4616
+ const { intents, warnings } = resolveTableQueryIntents(
4617
+ CadenzaService.serviceRegistry?.serviceName,
4618
+ tableName,
4619
+ table,
4620
+ schema
4621
+ );
4622
+ for (const warning of warnings) {
4623
+ CadenzaService.log(
4624
+ "Skipped custom query intent registration.",
4625
+ {
4626
+ tableName,
4627
+ warning
4628
+ },
4629
+ "warning"
4630
+ );
4631
+ }
4632
+ for (const intent of intents) {
4633
+ CadenzaService.defineIntent({
4634
+ name: intent.name,
4635
+ description: intent.description,
4636
+ input: intent.input
4637
+ });
4638
+ }
4639
+ task.respondsTo(...intents.map((intent) => intent.name));
3833
4640
  }
3834
4641
  }
3835
4642
  getInputSchema(op, tableName, table) {
@@ -3921,18 +4728,18 @@ function getInsertDataSchemaFromTable(table, tableName) {
3921
4728
  Object.entries(table.fields).map((field) => {
3922
4729
  return [
3923
4730
  field[0],
3924
- [
3925
- {
4731
+ {
4732
+ value: {
3926
4733
  type: tableFieldTypeToSchemaType(field[1].type),
3927
4734
  description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
3928
4735
  },
3929
- {
4736
+ effect: {
3930
4737
  type: "string",
3931
4738
  constraints: {
3932
4739
  oneOf: ["increment", "decrement", "set"]
3933
4740
  }
3934
4741
  },
3935
- {
4742
+ subOperation: {
3936
4743
  type: "object",
3937
4744
  properties: {
3938
4745
  subOperation: {
@@ -3942,17 +4749,17 @@ function getInsertDataSchemaFromTable(table, tableName) {
3942
4749
  table: {
3943
4750
  type: "string"
3944
4751
  },
3945
- data: [
3946
- {
4752
+ data: {
4753
+ single: {
3947
4754
  type: "object"
3948
4755
  },
3949
- {
4756
+ batch: {
3950
4757
  type: "array",
3951
4758
  items: {
3952
4759
  type: "object"
3953
4760
  }
3954
4761
  }
3955
- ],
4762
+ },
3956
4763
  filter: {
3957
4764
  type: "object"
3958
4765
  },
@@ -3968,7 +4775,7 @@ function getInsertDataSchemaFromTable(table, tableName) {
3968
4775
  },
3969
4776
  required: ["subOperation", "table"]
3970
4777
  }
3971
- ]
4778
+ }
3972
4779
  ];
3973
4780
  })
3974
4781
  )
@@ -3976,13 +4783,13 @@ function getInsertDataSchemaFromTable(table, tableName) {
3976
4783
  required: Object.entries(table.fields).filter((field) => field[1].required || field[1].primary).map((field) => field[0]),
3977
4784
  strict: true
3978
4785
  };
3979
- return [
3980
- dataSchema,
3981
- {
4786
+ return {
4787
+ single: dataSchema,
4788
+ batch: {
3982
4789
  type: "array",
3983
4790
  items: dataSchema
3984
4791
  }
3985
- ];
4792
+ };
3986
4793
  }
3987
4794
  function getQueryFilterSchemaFromTable(table, tableName) {
3988
4795
  return {
@@ -3992,24 +4799,24 @@ function getQueryFilterSchemaFromTable(table, tableName) {
3992
4799
  Object.entries(table.fields).map((field) => {
3993
4800
  return [
3994
4801
  field[0],
3995
- [
3996
- {
4802
+ {
4803
+ value: {
3997
4804
  type: tableFieldTypeToSchemaType(field[1].type),
3998
4805
  description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
3999
4806
  },
4000
- {
4807
+ in: {
4001
4808
  type: "array",
4002
4809
  items: {
4003
4810
  type: tableFieldTypeToSchemaType(field[1].type)
4004
4811
  }
4005
4812
  }
4006
- ]
4813
+ }
4007
4814
  ];
4008
4815
  })
4009
4816
  )
4010
4817
  },
4011
4818
  strict: true,
4012
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4819
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
4013
4820
  };
4014
4821
  }
4015
4822
  function getQueryFieldsSchemaFromTable(table, tableName) {
@@ -4021,7 +4828,7 @@ function getQueryFieldsSchemaFromTable(table, tableName) {
4021
4828
  oneOf: Object.keys(table.fields)
4022
4829
  }
4023
4830
  },
4024
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4831
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
4025
4832
  };
4026
4833
  }
4027
4834
  function getQueryJoinsSchemaFromTable(table, tableName) {
@@ -4068,7 +4875,7 @@ function getQueryJoinsSchemaFromTable(table, tableName) {
4068
4875
  )
4069
4876
  },
4070
4877
  strict: true,
4071
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4878
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
4072
4879
  };
4073
4880
  }
4074
4881
  function getQuerySortSchemaFromTable(table, tableName) {
@@ -4090,7 +4897,7 @@ function getQuerySortSchemaFromTable(table, tableName) {
4090
4897
  )
4091
4898
  },
4092
4899
  strict: true,
4093
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4900
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
4094
4901
  };
4095
4902
  }
4096
4903
  function getQueryLimitSchemaFromTable() {
@@ -4152,12 +4959,10 @@ function getQueryOnConflictSchemaFromTable(table, tableName) {
4152
4959
  Object.entries(table.fields).map((field) => {
4153
4960
  return [
4154
4961
  field[0],
4155
- [
4156
- {
4157
- type: tableFieldTypeToSchemaType(field[1].type),
4158
- description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
4159
- }
4160
- ]
4962
+ {
4963
+ type: tableFieldTypeToSchemaType(field[1].type),
4964
+ description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
4965
+ }
4161
4966
  ];
4162
4967
  })
4163
4968
  )
@@ -4515,6 +5320,75 @@ var GraphSyncController = class _GraphSyncController {
4515
5320
  { concurrency: 30 }
4516
5321
  ) : CadenzaService.get("dbInsertSignalToTaskMap"))?.then(registerSignalTask)
4517
5322
  );
5323
+ const registerIntentTask = CadenzaService.createMetaTask(
5324
+ "Record intent registration",
5325
+ (ctx) => {
5326
+ if (!ctx.__syncing) {
5327
+ return;
5328
+ }
5329
+ CadenzaService.debounce("meta.sync_controller.synced_resource", {
5330
+ delayMs: 3e3
5331
+ });
5332
+ const task = CadenzaService.get(ctx.__taskName);
5333
+ task.__registeredIntents = task.__registeredIntents ?? /* @__PURE__ */ new Set();
5334
+ task.__registeredIntents.add(ctx.__intent);
5335
+ }
5336
+ );
5337
+ this.registerIntentToTaskMapTask = CadenzaService.createMetaTask(
5338
+ "Split intents of task",
5339
+ function* (ctx) {
5340
+ const task = ctx.task;
5341
+ if (task.hidden || !task.register) return;
5342
+ task.__registeredIntents = task.__registeredIntents ?? /* @__PURE__ */ new Set();
5343
+ task.__invalidMetaIntentWarnings = task.__invalidMetaIntentWarnings ?? /* @__PURE__ */ new Set();
5344
+ for (const intent of task.handlesIntents) {
5345
+ if (task.__registeredIntents.has(intent)) continue;
5346
+ if (isMetaIntentName(intent) && !task.isMeta) {
5347
+ if (!task.__invalidMetaIntentWarnings.has(intent)) {
5348
+ task.__invalidMetaIntentWarnings.add(intent);
5349
+ CadenzaService.log(
5350
+ "Skipping intent-to-task registration: non-meta task cannot handle meta intent.",
5351
+ {
5352
+ intent,
5353
+ taskName: task.name,
5354
+ taskVersion: task.version
5355
+ },
5356
+ "warning"
5357
+ );
5358
+ }
5359
+ continue;
5360
+ }
5361
+ yield {
5362
+ data: {
5363
+ intentName: intent,
5364
+ taskName: task.name,
5365
+ taskVersion: task.version,
5366
+ serviceName: CadenzaService.serviceRegistry.serviceName
5367
+ },
5368
+ __taskName: task.name,
5369
+ __intent: intent
5370
+ };
5371
+ }
5372
+ }
5373
+ ).then(
5374
+ (this.isCadenzaDBReady ? CadenzaService.createCadenzaDBInsertTask(
5375
+ "intent_to_task_map",
5376
+ {
5377
+ onConflict: {
5378
+ target: [
5379
+ "intent_name",
5380
+ "task_name",
5381
+ "task_version",
5382
+ "service_name"
5383
+ ],
5384
+ action: {
5385
+ do: "nothing"
5386
+ }
5387
+ }
5388
+ },
5389
+ { concurrency: 30 }
5390
+ ) : CadenzaService.get("dbInsertIntentToTaskMap"))?.then(registerIntentTask)
5391
+ );
4518
5392
  this.registerTaskMapTask = CadenzaService.createMetaTask(
4519
5393
  "Register task map to DB",
4520
5394
  function* (ctx) {
@@ -4637,6 +5511,7 @@ var GraphSyncController = class _GraphSyncController {
4637
5511
  CadenzaService.registry.doForEachTask.clone().doOn("meta.sync_controller.synced_tasks").then(
4638
5512
  this.registerTaskMapTask,
4639
5513
  this.registerSignalToTaskMapTask,
5514
+ this.registerIntentToTaskMapTask,
4640
5515
  this.registerDeputyRelationshipTask
4641
5516
  );
4642
5517
  CadenzaService.registry.getAllRoutines.clone().doOn("meta.sync_controller.synced_routines").then(this.splitTasksInRoutines);
@@ -4778,11 +5653,192 @@ var CadenzaService = class {
4778
5653
  import_core3.default.interval(signal, context, intervalMs, leading, startDateTime);
4779
5654
  }
4780
5655
  static defineIntent(intent) {
4781
- this.inquiryBroker?.intents.set(intent.name, intent);
5656
+ this.inquiryBroker?.addIntent(intent);
4782
5657
  return intent;
4783
5658
  }
4784
- static async inquire(inquiry, context, options) {
4785
- return this.inquiryBroker?.inquire(inquiry, context, options);
5659
+ static getInquiryResponderDescriptor(task) {
5660
+ return this.serviceRegistry.getInquiryResponderDescriptor(task);
5661
+ }
5662
+ static compareInquiryResponders(left, right) {
5663
+ return compareResponderDescriptors(left.descriptor, right.descriptor);
5664
+ }
5665
+ static buildInquirySummary(inquiry, startedAt, statuses, totalResponders) {
5666
+ const counts = summarizeResponderStatuses(statuses);
5667
+ const isMetaInquiry = isMetaIntentName(inquiry);
5668
+ const eligibleResponders = statuses.length;
5669
+ return {
5670
+ inquiry,
5671
+ isMetaInquiry,
5672
+ totalResponders,
5673
+ eligibleResponders,
5674
+ filteredOutResponders: Math.max(0, totalResponders - eligibleResponders),
5675
+ responded: counts.responded,
5676
+ failed: counts.failed,
5677
+ timedOut: counts.timedOut,
5678
+ pending: counts.pending,
5679
+ durationMs: Date.now() - startedAt,
5680
+ responders: statuses
5681
+ };
5682
+ }
5683
+ static async inquire(inquiry, context, options = {}) {
5684
+ this.bootstrap();
5685
+ const observer = this.inquiryBroker?.inquiryObservers.get(inquiry);
5686
+ const allResponders = observer ? Array.from(observer.tasks).map((task) => ({
5687
+ task,
5688
+ descriptor: this.getInquiryResponderDescriptor(task)
5689
+ })) : [];
5690
+ const isMetaInquiry = isMetaIntentName(inquiry);
5691
+ const responders = allResponders.filter(({ task, descriptor }) => {
5692
+ const shouldExecute = shouldExecuteInquiryResponder(inquiry, task.isMeta);
5693
+ if (shouldExecute) {
5694
+ return true;
5695
+ }
5696
+ const warningKey = `${inquiry}|${descriptor.serviceName}|${descriptor.taskName}|${descriptor.taskVersion}|${descriptor.localTaskName}`;
5697
+ if (!this.warnedInvalidMetaIntentResponderKeys.has(warningKey)) {
5698
+ this.warnedInvalidMetaIntentResponderKeys.add(warningKey);
5699
+ this.log(
5700
+ "Skipping non-meta task for meta intent inquiry.",
5701
+ {
5702
+ inquiry,
5703
+ responder: descriptor
5704
+ },
5705
+ "warning",
5706
+ descriptor.serviceName
5707
+ );
5708
+ }
5709
+ return false;
5710
+ });
5711
+ if (responders.length === 0) {
5712
+ return {
5713
+ __inquiryMeta: {
5714
+ inquiry,
5715
+ isMetaInquiry,
5716
+ totalResponders: allResponders.length,
5717
+ eligibleResponders: 0,
5718
+ filteredOutResponders: allResponders.length,
5719
+ responded: 0,
5720
+ failed: 0,
5721
+ timedOut: 0,
5722
+ pending: 0,
5723
+ durationMs: 0,
5724
+ responders: []
5725
+ }
5726
+ };
5727
+ }
5728
+ responders.sort(this.compareInquiryResponders.bind(this));
5729
+ const overallTimeoutMs = options.overallTimeoutMs ?? options.timeout ?? 0;
5730
+ const requireComplete = options.requireComplete ?? false;
5731
+ const perResponderTimeoutMs = options.perResponderTimeoutMs;
5732
+ const startedAt = Date.now();
5733
+ const statuses = [];
5734
+ const statusByTask = /* @__PURE__ */ new Map();
5735
+ for (const responder of responders) {
5736
+ const status = {
5737
+ ...responder.descriptor,
5738
+ status: "timed_out",
5739
+ durationMs: 0
5740
+ };
5741
+ statuses.push(status);
5742
+ statusByTask.set(responder.task, status);
5743
+ }
5744
+ const resultsByTask = /* @__PURE__ */ new Map();
5745
+ const resolverTasks = [];
5746
+ const pending = new Set(responders.map((r) => r.task));
5747
+ const startTimeByTask = /* @__PURE__ */ new Map();
5748
+ this.emit("meta.inquiry_broker.inquire", { inquiry, context });
5749
+ return new Promise((resolve, reject) => {
5750
+ let finalized = false;
5751
+ let timeoutId;
5752
+ const finalize = (timedOut) => {
5753
+ if (finalized) return;
5754
+ finalized = true;
5755
+ if (timeoutId) {
5756
+ clearTimeout(timeoutId);
5757
+ timeoutId = void 0;
5758
+ }
5759
+ for (const resolverTask of resolverTasks) {
5760
+ resolverTask.destroy();
5761
+ }
5762
+ if (timedOut && pending.size > 0) {
5763
+ for (const task of pending) {
5764
+ const status = statusByTask.get(task);
5765
+ if (!status) continue;
5766
+ status.status = "timed_out";
5767
+ status.durationMs = Date.now() - (startTimeByTask.get(task) ?? startedAt);
5768
+ }
5769
+ }
5770
+ const fulfilledContexts = responders.filter((responder) => resultsByTask.has(responder.task)).map((responder) => resultsByTask.get(responder.task));
5771
+ const mergedContext = mergeInquiryContexts(fulfilledContexts);
5772
+ const inquiryMeta = this.buildInquirySummary(
5773
+ inquiry,
5774
+ startedAt,
5775
+ statuses,
5776
+ allResponders.length
5777
+ );
5778
+ const responseContext = {
5779
+ ...mergedContext,
5780
+ __inquiryMeta: inquiryMeta
5781
+ };
5782
+ if (requireComplete && (timedOut || inquiryMeta.failed > 0 || inquiryMeta.timedOut > 0 || inquiryMeta.pending > 0)) {
5783
+ reject({
5784
+ ...responseContext,
5785
+ __error: `Inquiry '${inquiry}' did not complete successfully`,
5786
+ errored: true
5787
+ });
5788
+ return;
5789
+ }
5790
+ resolve(responseContext);
5791
+ };
5792
+ if (overallTimeoutMs > 0) {
5793
+ timeoutId = setTimeout(() => finalize(true), overallTimeoutMs);
5794
+ }
5795
+ for (const responder of responders) {
5796
+ const { task, descriptor } = responder;
5797
+ const inquiryId = (0, import_uuid3.v4)();
5798
+ startTimeByTask.set(task, Date.now());
5799
+ const resolverTask = this.createEphemeralMetaTask(
5800
+ `Resolve inquiry ${inquiry} for ${descriptor.localTaskName}`,
5801
+ (resultCtx) => {
5802
+ if (finalized) {
5803
+ return;
5804
+ }
5805
+ pending.delete(task);
5806
+ const status = statusByTask.get(task);
5807
+ if (status) {
5808
+ status.durationMs = Date.now() - (startTimeByTask.get(task) ?? startedAt);
5809
+ if (resultCtx?.errored || resultCtx?.failed) {
5810
+ status.status = "failed";
5811
+ status.error = String(
5812
+ resultCtx?.__error ?? resultCtx?.error ?? "Inquiry responder failed"
5813
+ );
5814
+ } else {
5815
+ status.status = "fulfilled";
5816
+ resultsByTask.set(task, resultCtx);
5817
+ }
5818
+ }
5819
+ if (pending.size === 0) {
5820
+ finalize(false);
5821
+ }
5822
+ },
5823
+ "Resolves distributed inquiry responder result",
5824
+ { register: false }
5825
+ ).doOn(`meta.node.graph_completed:${inquiryId}`);
5826
+ resolverTasks.push(resolverTask);
5827
+ const executionContext = {
5828
+ ...context,
5829
+ __routineExecId: inquiryId,
5830
+ __isInquiry: true
5831
+ };
5832
+ if (perResponderTimeoutMs !== void 0) {
5833
+ executionContext.__timeout = perResponderTimeoutMs;
5834
+ }
5835
+ if (task.isMeta) {
5836
+ this.metaRunner?.run(task, executionContext);
5837
+ } else {
5838
+ this.runner?.run(task, executionContext);
5839
+ }
5840
+ }
5841
+ });
4786
5842
  }
4787
5843
  /**
4788
5844
  * Executes the given task or graph routine within the provided context using the configured runner.
@@ -5819,6 +6875,7 @@ var CadenzaService = class {
5819
6875
  };
5820
6876
  CadenzaService.isBootstrapped = false;
5821
6877
  CadenzaService.serviceCreated = false;
6878
+ CadenzaService.warnedInvalidMetaIntentResponderKeys = /* @__PURE__ */ new Set();
5822
6879
 
5823
6880
  // src/index.ts
5824
6881
  var import_core4 = require("@cadenza.io/core");