@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.mjs CHANGED
@@ -247,7 +247,74 @@ var DatabaseTask = class extends DeputyTask {
247
247
  var isNode = typeof process !== "undefined" && process.versions?.node != null;
248
248
  var isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
249
249
 
250
+ // src/utils/inquiry.ts
251
+ var META_INTENT_PREFIX = "meta-";
252
+ var META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT = "meta-runtime-transport-diagnostics";
253
+ function isPlainObject(value) {
254
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
255
+ }
256
+ function deepMergeDeterministic(left, right) {
257
+ if (Array.isArray(left) && Array.isArray(right)) {
258
+ return [...left, ...right];
259
+ }
260
+ if (isPlainObject(left) && isPlainObject(right)) {
261
+ const merged = { ...left };
262
+ const keys = Array.from(/* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)])).sort();
263
+ for (const key of keys) {
264
+ if (!(key in left)) {
265
+ merged[key] = right[key];
266
+ continue;
267
+ }
268
+ if (!(key in right)) {
269
+ merged[key] = left[key];
270
+ continue;
271
+ }
272
+ merged[key] = deepMergeDeterministic(left[key], right[key]);
273
+ }
274
+ return merged;
275
+ }
276
+ return right;
277
+ }
278
+ function mergeInquiryContexts(contexts) {
279
+ return contexts.reduce((acc, next) => deepMergeDeterministic(acc, next), {});
280
+ }
281
+ function isMetaIntentName(intentName) {
282
+ return intentName.startsWith(META_INTENT_PREFIX);
283
+ }
284
+ function shouldExecuteInquiryResponder(inquiry, responderIsMeta) {
285
+ if (!isMetaIntentName(inquiry)) {
286
+ return true;
287
+ }
288
+ return responderIsMeta;
289
+ }
290
+ function compareResponderDescriptors(left, right) {
291
+ if (left.serviceName !== right.serviceName) {
292
+ return left.serviceName.localeCompare(right.serviceName);
293
+ }
294
+ if (left.taskName !== right.taskName) {
295
+ return left.taskName.localeCompare(right.taskName);
296
+ }
297
+ if (left.taskVersion !== right.taskVersion) {
298
+ return left.taskVersion - right.taskVersion;
299
+ }
300
+ return left.localTaskName.localeCompare(right.localTaskName);
301
+ }
302
+ function summarizeResponderStatuses(statuses) {
303
+ let responded = 0;
304
+ let failed = 0;
305
+ let timedOut = 0;
306
+ let pending = 0;
307
+ for (const status of statuses) {
308
+ if (status.status === "fulfilled") responded++;
309
+ if (status.status === "failed") failed++;
310
+ if (status.status === "timed_out") timedOut++;
311
+ }
312
+ pending = Math.max(0, statuses.length - responded - failed - timedOut);
313
+ return { responded, failed, timedOut, pending };
314
+ }
315
+
250
316
  // src/registry/ServiceRegistry.ts
317
+ var META_SERVICE_REGISTRY_FULL_SYNC_INTENT = "meta-service-registry-full-sync";
251
318
  var ServiceRegistry = class _ServiceRegistry {
252
319
  /**
253
320
  * Initializes a private constructor for managing service instances, remote signals,
@@ -262,11 +329,47 @@ var ServiceRegistry = class _ServiceRegistry {
262
329
  this.instances = /* @__PURE__ */ new Map();
263
330
  this.deputies = /* @__PURE__ */ new Map();
264
331
  this.remoteSignals = /* @__PURE__ */ new Map();
332
+ this.remoteIntents = /* @__PURE__ */ new Map();
333
+ this.remoteIntentDeputiesByKey = /* @__PURE__ */ new Map();
334
+ this.remoteIntentDeputiesByTask = /* @__PURE__ */ new Map();
265
335
  this.serviceName = null;
266
336
  this.serviceInstanceId = null;
267
337
  this.numberOfRunningGraphs = 0;
268
338
  this.useSocket = false;
269
339
  this.retryCount = 3;
340
+ CadenzaService.defineIntent({
341
+ name: META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT,
342
+ description: "Gather transport diagnostics across all services and communication clients.",
343
+ input: {
344
+ type: "object",
345
+ properties: {
346
+ detailLevel: {
347
+ type: "string",
348
+ constraints: {
349
+ oneOf: ["summary", "full"]
350
+ }
351
+ },
352
+ includeErrorHistory: {
353
+ type: "boolean"
354
+ },
355
+ errorHistoryLimit: {
356
+ type: "number",
357
+ constraints: {
358
+ min: 1,
359
+ max: 200
360
+ }
361
+ }
362
+ }
363
+ },
364
+ output: {
365
+ type: "object",
366
+ properties: {
367
+ transportDiagnostics: {
368
+ type: "object"
369
+ }
370
+ }
371
+ }
372
+ });
270
373
  this.handleInstanceUpdateTask = CadenzaService.createMetaTask(
271
374
  "Handle Instance Update",
272
375
  (ctx, emit) => {
@@ -306,7 +409,7 @@ var ServiceRegistry = class _ServiceRegistry {
306
409
  if (this.serviceName === serviceName) {
307
410
  return false;
308
411
  }
309
- if (!isFrontend && this.deputies.has(serviceName) || this.remoteSignals.has(serviceName)) {
412
+ if (!isFrontend && (this.deputies.has(serviceName) || this.remoteIntents.has(serviceName)) || this.remoteSignals.has(serviceName)) {
310
413
  const clientCreated = instances?.some(
311
414
  (i) => i.address === address && i.port === port && i.clientCreated && i.isActive
312
415
  );
@@ -356,7 +459,10 @@ var ServiceRegistry = class _ServiceRegistry {
356
459
  for (const serviceInstance of ctx.serviceInstances) {
357
460
  yield { serviceInstance };
358
461
  }
359
- }).doOn("meta.service_registry.registered_global_signals").then(this.handleInstanceUpdateTask);
462
+ }).doOn(
463
+ "meta.service_registry.registered_global_signals",
464
+ "meta.service_registry.registered_global_intents"
465
+ ).then(this.handleInstanceUpdateTask);
360
466
  this.handleGlobalSignalRegistrationTask = CadenzaService.createMetaTask(
361
467
  "Handle global Signal Registration",
362
468
  (ctx) => {
@@ -397,6 +503,32 @@ var ServiceRegistry = class _ServiceRegistry {
397
503
  },
398
504
  "Handles registration of remote signals"
399
505
  ).emits("meta.service_registry.registered_global_signals").doOn("global.meta.cadenza_db.gathered_sync_data");
506
+ this.handleGlobalIntentRegistrationTask = CadenzaService.createMetaTask(
507
+ "Handle global intent registration",
508
+ (ctx) => {
509
+ const intentToTaskMaps = this.normalizeIntentMaps(ctx);
510
+ const sorted = intentToTaskMaps.sort((a, b) => {
511
+ if (a.deleted && !b.deleted) return -1;
512
+ if (!a.deleted && b.deleted) return 1;
513
+ return 0;
514
+ });
515
+ for (const map of sorted) {
516
+ if (map.deleted) {
517
+ this.unregisterRemoteIntentDeputy(map);
518
+ continue;
519
+ }
520
+ CadenzaService.inquiryBroker.addIntent({
521
+ name: map.intentName
522
+ });
523
+ this.registerRemoteIntentDeputy(map);
524
+ }
525
+ return true;
526
+ },
527
+ "Handles registration of remote inquiry intent responders"
528
+ ).emits("meta.service_registry.registered_global_intents").doOn(
529
+ "global.meta.cadenza_db.gathered_sync_data",
530
+ "global.meta.graph_metadata.task_intent_associated"
531
+ );
400
532
  this.handleServiceNotRespondingTask = CadenzaService.createMetaTask(
401
533
  "Handle service not responding",
402
534
  (ctx, emit) => {
@@ -501,45 +633,58 @@ var ServiceRegistry = class _ServiceRegistry {
501
633
  },
502
634
  "Handles status update from socket broadcast"
503
635
  ).doOn("meta.socket_client.status_received");
504
- const mergeSyncDataTask = CadenzaService.createUniqueMetaTask(
505
- "Merge sync data",
506
- (ctx) => {
507
- let joinedContext = {};
508
- ctx.joinedContexts.forEach((ctx2) => {
509
- joinedContext = { ...joinedContext, ...ctx2 };
510
- });
511
- return joinedContext;
512
- }
513
- ).emits("meta.service_registry.initial_sync_complete").then(this.handleGlobalSignalRegistrationTask);
514
- this.fullSyncTask = CadenzaService.createMetaRoutine("Full sync", [
515
- CadenzaService.createCadenzaDBQueryTask("signal_to_task_map", {
516
- filter: {
517
- isGlobal: true
518
- },
519
- fields: ["signal_name", "service_name", "deleted"]
520
- }).then(mergeSyncDataTask),
521
- CadenzaService.createCadenzaDBQueryTask("service_instance", {
522
- filter: {
523
- deleted: false,
524
- is_active: true,
525
- is_non_responsive: false,
526
- is_blocked: false
527
- },
528
- fields: [
529
- "uuid",
530
- "address",
531
- "port",
532
- "service_name",
533
- "is_active",
534
- "is_non_responsive",
535
- "is_blocked",
536
- "health",
537
- "exposed",
538
- "created",
539
- "is_frontend"
540
- ]
541
- }).then(mergeSyncDataTask)
542
- ]).doOn("meta.sync_requested");
636
+ this.fullSyncTask = CadenzaService.createMetaTask(
637
+ "Full sync",
638
+ async (ctx) => {
639
+ const inquiryResult = await CadenzaService.inquire(
640
+ META_SERVICE_REGISTRY_FULL_SYNC_INTENT,
641
+ {
642
+ syncScope: "service-registry-full-sync"
643
+ },
644
+ ctx.inquiryOptions ?? ctx.__inquiryOptions ?? {}
645
+ );
646
+ const signalToTaskMaps = (inquiryResult.signalToTaskMaps ?? []).filter((m) => !!m.isGlobal).map((m) => ({
647
+ signalName: m.signalName,
648
+ serviceName: m.serviceName,
649
+ deleted: !!m.deleted
650
+ }));
651
+ const intentToTaskMaps = (inquiryResult.intentToTaskMaps ?? []).map(
652
+ (m) => ({
653
+ intentName: m.intentName,
654
+ taskName: m.taskName,
655
+ taskVersion: m.taskVersion ?? 1,
656
+ serviceName: m.serviceName,
657
+ deleted: !!m.deleted
658
+ })
659
+ );
660
+ const serviceInstances = (inquiryResult.serviceInstances ?? []).filter(
661
+ (instance) => !instance.deleted && !!instance.isActive && !instance.isNonResponsive && !instance.isBlocked
662
+ ).map((instance) => ({
663
+ uuid: instance.uuid,
664
+ address: instance.address,
665
+ port: instance.port,
666
+ serviceName: instance.serviceName,
667
+ isActive: !!instance.isActive,
668
+ isNonResponsive: !!instance.isNonResponsive,
669
+ isBlocked: !!instance.isBlocked,
670
+ health: instance.health ?? {},
671
+ exposed: !!instance.exposed,
672
+ created: instance.created,
673
+ isFrontend: !!instance.isFrontend
674
+ }));
675
+ return {
676
+ ...ctx,
677
+ signalToTaskMaps,
678
+ intentToTaskMaps,
679
+ serviceInstances,
680
+ __inquiryMeta: inquiryResult.__inquiryMeta
681
+ };
682
+ },
683
+ "Runs service registry full sync through one distributed inquiry intent."
684
+ ).doOn("meta.sync_requested").emits("meta.service_registry.initial_sync_complete").then(
685
+ this.handleGlobalSignalRegistrationTask,
686
+ this.handleGlobalIntentRegistrationTask
687
+ );
543
688
  this.getInstanceById = CadenzaService.createMetaTask(
544
689
  "Get instance by id",
545
690
  (context) => {
@@ -708,6 +853,25 @@ var ServiceRegistry = class _ServiceRegistry {
708
853
  __active: self?.isActive ?? false
709
854
  };
710
855
  }).doOn("meta.socket.status_check_requested");
856
+ this.collectTransportDiagnosticsTask = CadenzaService.createMetaTask(
857
+ "Collect transport diagnostics",
858
+ async (ctx) => {
859
+ const inquiryResult = await CadenzaService.inquire(
860
+ META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT,
861
+ {
862
+ detailLevel: ctx.detailLevel,
863
+ includeErrorHistory: ctx.includeErrorHistory,
864
+ errorHistoryLimit: ctx.errorHistoryLimit
865
+ },
866
+ ctx.inquiryOptions ?? ctx.__inquiryOptions ?? {}
867
+ );
868
+ return {
869
+ ...ctx,
870
+ ...inquiryResult
871
+ };
872
+ },
873
+ "Collects distributed transport diagnostics using inquiry responders."
874
+ ).doOn("meta.service_registry.transport_diagnostics_requested").emits("meta.service_registry.transport_diagnostics_collected").emitsOnFail("meta.service_registry.transport_diagnostics_failed");
711
875
  this.insertServiceTask = CadenzaService.createCadenzaDBInsertTask(
712
876
  "service",
713
877
  {
@@ -916,8 +1080,125 @@ var ServiceRegistry = class _ServiceRegistry {
916
1080
  if (!this._instance) this._instance = new _ServiceRegistry();
917
1081
  return this._instance;
918
1082
  }
1083
+ buildRemoteIntentDeputyKey(map) {
1084
+ return `${map.intentName}|${map.serviceName}|${map.taskName}|${map.taskVersion ?? 1}`;
1085
+ }
1086
+ normalizeIntentMaps(ctx) {
1087
+ if (Array.isArray(ctx.intentToTaskMaps)) {
1088
+ return ctx.intentToTaskMaps.map((m) => ({
1089
+ intentName: m.intentName ?? m.intent_name,
1090
+ serviceName: m.serviceName ?? m.service_name,
1091
+ taskName: m.taskName ?? m.task_name,
1092
+ taskVersion: m.taskVersion ?? m.task_version ?? 1,
1093
+ deleted: !!m.deleted
1094
+ })).filter((m) => m.intentName && m.serviceName && m.taskName);
1095
+ }
1096
+ const single = ctx.intentToTaskMap ?? ctx.data ?? (ctx.intentName ? ctx : void 0);
1097
+ if (!single) return [];
1098
+ const normalized = {
1099
+ intentName: single.intentName ?? single.intent_name,
1100
+ serviceName: single.serviceName ?? single.service_name,
1101
+ taskName: single.taskName ?? single.task_name,
1102
+ taskVersion: single.taskVersion ?? single.task_version ?? 1,
1103
+ deleted: !!single.deleted
1104
+ };
1105
+ if (!normalized.intentName || !normalized.serviceName || !normalized.taskName)
1106
+ return [];
1107
+ return [normalized];
1108
+ }
1109
+ registerRemoteIntentDeputy(map) {
1110
+ if (!this.serviceName || map.serviceName === this.serviceName) {
1111
+ return;
1112
+ }
1113
+ const key = this.buildRemoteIntentDeputyKey(map);
1114
+ if (this.remoteIntentDeputiesByKey.has(key)) {
1115
+ return;
1116
+ }
1117
+ const deputyTaskName = `Inquire ${map.intentName} via ${map.serviceName} (${map.taskName} v${map.taskVersion})`;
1118
+ const deputyTask = isMetaIntentName(map.intentName) ? CadenzaService.createMetaDeputyTask(map.taskName, map.serviceName, {
1119
+ register: false,
1120
+ isHidden: true,
1121
+ retryCount: 1,
1122
+ retryDelay: 50,
1123
+ retryDelayFactor: 1.2
1124
+ }) : CadenzaService.createDeputyTask(map.taskName, map.serviceName, {
1125
+ register: false,
1126
+ isHidden: true,
1127
+ retryCount: 1,
1128
+ retryDelay: 50,
1129
+ retryDelayFactor: 1.2
1130
+ });
1131
+ deputyTask.respondsTo(map.intentName);
1132
+ if (!this.remoteIntents.has(map.serviceName)) {
1133
+ this.remoteIntents.set(map.serviceName, /* @__PURE__ */ new Set());
1134
+ }
1135
+ this.remoteIntents.get(map.serviceName).add(map.intentName);
1136
+ const descriptor = {
1137
+ key,
1138
+ intentName: map.intentName,
1139
+ serviceName: map.serviceName,
1140
+ remoteTaskName: map.taskName,
1141
+ remoteTaskVersion: map.taskVersion,
1142
+ localTaskName: deputyTask.name || deputyTaskName,
1143
+ localTask: deputyTask
1144
+ };
1145
+ this.remoteIntentDeputiesByKey.set(key, descriptor);
1146
+ this.remoteIntentDeputiesByTask.set(deputyTask, descriptor);
1147
+ }
1148
+ unregisterRemoteIntentDeputy(map) {
1149
+ const key = this.buildRemoteIntentDeputyKey(map);
1150
+ const descriptor = this.remoteIntentDeputiesByKey.get(key);
1151
+ if (!descriptor) {
1152
+ return;
1153
+ }
1154
+ const task = descriptor.localTask;
1155
+ if (task) {
1156
+ CadenzaService.inquiryBroker.unsubscribe(descriptor.intentName, task);
1157
+ task.destroy();
1158
+ }
1159
+ this.remoteIntentDeputiesByTask.delete(descriptor.localTask);
1160
+ this.remoteIntentDeputiesByKey.delete(key);
1161
+ this.remoteIntents.get(descriptor.serviceName)?.delete(descriptor.intentName);
1162
+ if (!this.remoteIntents.get(descriptor.serviceName)?.size) {
1163
+ this.remoteIntents.delete(descriptor.serviceName);
1164
+ }
1165
+ const deputies = this.deputies.get(descriptor.serviceName);
1166
+ if (deputies) {
1167
+ this.deputies.set(
1168
+ descriptor.serviceName,
1169
+ deputies.filter((d) => d.localTaskName !== descriptor.localTaskName)
1170
+ );
1171
+ if (this.deputies.get(descriptor.serviceName)?.length === 0) {
1172
+ this.deputies.delete(descriptor.serviceName);
1173
+ }
1174
+ }
1175
+ }
1176
+ getInquiryResponderDescriptor(task) {
1177
+ const remote = this.remoteIntentDeputiesByTask.get(task);
1178
+ if (remote) {
1179
+ return {
1180
+ isRemote: true,
1181
+ serviceName: remote.serviceName,
1182
+ taskName: remote.remoteTaskName,
1183
+ taskVersion: remote.remoteTaskVersion,
1184
+ localTaskName: remote.localTaskName
1185
+ };
1186
+ }
1187
+ return {
1188
+ isRemote: false,
1189
+ serviceName: this.serviceName ?? "UnknownService",
1190
+ taskName: task.name,
1191
+ taskVersion: task.version,
1192
+ localTaskName: task.name
1193
+ };
1194
+ }
919
1195
  reset() {
920
1196
  this.instances.clear();
1197
+ this.deputies.clear();
1198
+ this.remoteSignals.clear();
1199
+ this.remoteIntents.clear();
1200
+ this.remoteIntentDeputiesByKey.clear();
1201
+ this.remoteIntentDeputiesByTask.clear();
921
1202
  }
922
1203
  };
923
1204
 
@@ -1034,6 +1315,8 @@ var RestController = class _RestController {
1034
1315
  * It initializes and configures the REST server tasks.
1035
1316
  */
1036
1317
  constructor() {
1318
+ this.fetchClientDiagnostics = /* @__PURE__ */ new Map();
1319
+ this.diagnosticsErrorHistoryLimit = 100;
1037
1320
  /**
1038
1321
  * Fetches data from the given URL with a specified timeout. This function performs
1039
1322
  * a fetch request with the ability to cancel the request if it exceeds the provided timeout duration.
@@ -1074,6 +1357,11 @@ var RestController = class _RestController {
1074
1357
  "meta.rest.delegation_requested",
1075
1358
  "meta.socket.delegation_requested"
1076
1359
  );
1360
+ CadenzaService.createMetaTask(
1361
+ "Collect fetch transport diagnostics",
1362
+ (ctx) => this.collectFetchTransportDiagnostics(ctx),
1363
+ "Responds to distributed transport diagnostics inquiries with REST/fetch client data."
1364
+ ).respondsTo(META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT);
1077
1365
  CadenzaService.createMetaRoutine(
1078
1366
  "RestServer",
1079
1367
  [
@@ -1404,6 +1692,13 @@ var RestController = class _RestController {
1404
1692
  const port = protocol === "https" ? 443 : servicePort;
1405
1693
  const URL = `${protocol}://${serviceAddress}:${port}`;
1406
1694
  const fetchId = `${serviceAddress}_${port}`;
1695
+ const fetchDiagnostics = this.ensureFetchClientDiagnostics(
1696
+ fetchId,
1697
+ serviceName,
1698
+ URL
1699
+ );
1700
+ fetchDiagnostics.destroyed = false;
1701
+ fetchDiagnostics.updatedAt = Date.now();
1407
1702
  if (CadenzaService.get(`Send Handshake to ${URL}`)) {
1408
1703
  console.error("Fetch client already exists", URL);
1409
1704
  return;
@@ -1425,6 +1720,10 @@ var RestController = class _RestController {
1425
1720
  );
1426
1721
  if (response.__status !== "success") {
1427
1722
  const error = response.__error ?? `Failed to connect to service ${serviceName} ${ctx2.serviceInstanceId}`;
1723
+ fetchDiagnostics.connected = false;
1724
+ fetchDiagnostics.lastHandshakeError = error;
1725
+ fetchDiagnostics.updatedAt = Date.now();
1726
+ this.recordFetchClientError(fetchId, serviceName, URL, error);
1428
1727
  CadenzaService.log(
1429
1728
  "Fetch handshake failed.",
1430
1729
  { error, serviceName, URL },
@@ -1434,6 +1733,11 @@ var RestController = class _RestController {
1434
1733
  return { ...ctx2, __error: error, errored: true };
1435
1734
  }
1436
1735
  ctx2.serviceInstanceId = response.__serviceInstanceId;
1736
+ fetchDiagnostics.connected = true;
1737
+ fetchDiagnostics.destroyed = false;
1738
+ fetchDiagnostics.lastHandshakeAt = (/* @__PURE__ */ new Date()).toISOString();
1739
+ fetchDiagnostics.lastHandshakeError = null;
1740
+ fetchDiagnostics.updatedAt = Date.now();
1437
1741
  CadenzaService.log("Fetch client connected.", {
1438
1742
  response,
1439
1743
  serviceName,
@@ -1449,6 +1753,10 @@ var RestController = class _RestController {
1449
1753
  });
1450
1754
  }
1451
1755
  } catch (e) {
1756
+ fetchDiagnostics.connected = false;
1757
+ fetchDiagnostics.lastHandshakeError = this.getErrorMessage(e);
1758
+ fetchDiagnostics.updatedAt = Date.now();
1759
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1452
1760
  CadenzaService.log(
1453
1761
  "Error in fetch handshake",
1454
1762
  { error: e, serviceName, URL, ctx: ctx2 },
@@ -1470,6 +1778,8 @@ var RestController = class _RestController {
1470
1778
  if (ctx2.__remoteRoutineName === void 0) {
1471
1779
  return;
1472
1780
  }
1781
+ fetchDiagnostics.delegationRequests++;
1782
+ fetchDiagnostics.updatedAt = Date.now();
1473
1783
  let resultContext;
1474
1784
  try {
1475
1785
  resultContext = await this.fetchDataWithTimeout(
@@ -1483,8 +1793,21 @@ var RestController = class _RestController {
1483
1793
  },
1484
1794
  3e4
1485
1795
  );
1796
+ if (resultContext?.errored || resultContext?.failed) {
1797
+ fetchDiagnostics.delegationFailures++;
1798
+ fetchDiagnostics.updatedAt = Date.now();
1799
+ this.recordFetchClientError(
1800
+ fetchId,
1801
+ serviceName,
1802
+ URL,
1803
+ resultContext?.__error ?? resultContext?.error ?? "Delegation failed"
1804
+ );
1805
+ }
1486
1806
  } catch (e) {
1487
1807
  console.error("Error in delegation", e);
1808
+ fetchDiagnostics.delegationFailures++;
1809
+ fetchDiagnostics.updatedAt = Date.now();
1810
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1488
1811
  resultContext = {
1489
1812
  __error: `Error: ${e}`,
1490
1813
  errored: true,
@@ -1510,6 +1833,8 @@ var RestController = class _RestController {
1510
1833
  if (ctx2.__signalName === void 0) {
1511
1834
  return;
1512
1835
  }
1836
+ fetchDiagnostics.signalTransmissions++;
1837
+ fetchDiagnostics.updatedAt = Date.now();
1513
1838
  let response;
1514
1839
  try {
1515
1840
  response = await this.fetchDataWithTimeout(
@@ -1526,8 +1851,21 @@ var RestController = class _RestController {
1526
1851
  if (ctx2.__routineExecId) {
1527
1852
  emit(`meta.fetch.transmitted:${ctx2.__routineExecId}`, response);
1528
1853
  }
1854
+ if (response?.errored || response?.failed) {
1855
+ fetchDiagnostics.signalFailures++;
1856
+ fetchDiagnostics.updatedAt = Date.now();
1857
+ this.recordFetchClientError(
1858
+ fetchId,
1859
+ serviceName,
1860
+ URL,
1861
+ response?.__error ?? response?.error ?? "Signal transmission failed"
1862
+ );
1863
+ }
1529
1864
  } catch (e) {
1530
1865
  console.error("Error in transmission", e);
1866
+ fetchDiagnostics.signalFailures++;
1867
+ fetchDiagnostics.updatedAt = Date.now();
1868
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1531
1869
  response = {
1532
1870
  __error: `Error: ${e}`,
1533
1871
  errored: true,
@@ -1545,6 +1883,8 @@ var RestController = class _RestController {
1545
1883
  const statusTask = CadenzaService.createMetaTask(
1546
1884
  `Request status from ${URL}`,
1547
1885
  async (ctx2) => {
1886
+ fetchDiagnostics.statusChecks++;
1887
+ fetchDiagnostics.updatedAt = Date.now();
1548
1888
  let status;
1549
1889
  try {
1550
1890
  status = await this.fetchDataWithTimeout(
@@ -1554,7 +1894,20 @@ var RestController = class _RestController {
1554
1894
  },
1555
1895
  1e3
1556
1896
  );
1897
+ if (status?.errored || status?.failed) {
1898
+ fetchDiagnostics.statusFailures++;
1899
+ fetchDiagnostics.updatedAt = Date.now();
1900
+ this.recordFetchClientError(
1901
+ fetchId,
1902
+ serviceName,
1903
+ URL,
1904
+ status?.__error ?? status?.error ?? "Status check failed"
1905
+ );
1906
+ }
1557
1907
  } catch (e) {
1908
+ fetchDiagnostics.statusFailures++;
1909
+ fetchDiagnostics.updatedAt = Date.now();
1910
+ this.recordFetchClientError(fetchId, serviceName, URL, e);
1558
1911
  status = {
1559
1912
  __error: `Error: ${e}`,
1560
1913
  errored: true,
@@ -1566,6 +1919,9 @@ var RestController = class _RestController {
1566
1919
  "Requests status"
1567
1920
  ).doOn("meta.fetch.status_check_requested").emits("meta.fetch.status_checked").emitsOnFail("meta.fetch.status_check_failed");
1568
1921
  CadenzaService.createEphemeralMetaTask("Destroy fetch client", () => {
1922
+ fetchDiagnostics.connected = false;
1923
+ fetchDiagnostics.destroyed = true;
1924
+ fetchDiagnostics.updatedAt = Date.now();
1569
1925
  CadenzaService.log("Destroying fetch client", { URL, serviceName });
1570
1926
  handshakeTask.destroy();
1571
1927
  delegateTask.destroy();
@@ -1614,6 +1970,144 @@ var RestController = class _RestController {
1614
1970
  if (!this._instance) this._instance = new _RestController();
1615
1971
  return this._instance;
1616
1972
  }
1973
+ resolveTransportDiagnosticsOptions(ctx) {
1974
+ const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
1975
+ const includeErrorHistory = Boolean(ctx.includeErrorHistory);
1976
+ const requestedLimit = Number(ctx.errorHistoryLimit);
1977
+ const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
1978
+ return {
1979
+ detailLevel,
1980
+ includeErrorHistory,
1981
+ errorHistoryLimit
1982
+ };
1983
+ }
1984
+ ensureFetchClientDiagnostics(fetchId, serviceName, url) {
1985
+ let state = this.fetchClientDiagnostics.get(fetchId);
1986
+ if (!state) {
1987
+ state = {
1988
+ fetchId,
1989
+ serviceName,
1990
+ url,
1991
+ connected: false,
1992
+ destroyed: false,
1993
+ lastHandshakeAt: null,
1994
+ lastHandshakeError: null,
1995
+ lastError: null,
1996
+ lastErrorAt: 0,
1997
+ errorHistory: [],
1998
+ delegationRequests: 0,
1999
+ delegationFailures: 0,
2000
+ signalTransmissions: 0,
2001
+ signalFailures: 0,
2002
+ statusChecks: 0,
2003
+ statusFailures: 0,
2004
+ updatedAt: Date.now()
2005
+ };
2006
+ this.fetchClientDiagnostics.set(fetchId, state);
2007
+ } else {
2008
+ state.serviceName = serviceName;
2009
+ state.url = url;
2010
+ }
2011
+ return state;
2012
+ }
2013
+ getErrorMessage(error) {
2014
+ if (error instanceof Error) {
2015
+ return error.message;
2016
+ }
2017
+ if (typeof error === "string") {
2018
+ return error;
2019
+ }
2020
+ try {
2021
+ return JSON.stringify(error);
2022
+ } catch {
2023
+ return String(error);
2024
+ }
2025
+ }
2026
+ recordFetchClientError(fetchId, serviceName, url, error) {
2027
+ const state = this.ensureFetchClientDiagnostics(fetchId, serviceName, url);
2028
+ const message = this.getErrorMessage(error);
2029
+ const now = Date.now();
2030
+ state.lastError = message;
2031
+ state.lastErrorAt = now;
2032
+ state.updatedAt = now;
2033
+ state.errorHistory.push({ at: new Date(now).toISOString(), message });
2034
+ if (state.errorHistory.length > this.diagnosticsErrorHistoryLimit) {
2035
+ state.errorHistory.splice(
2036
+ 0,
2037
+ state.errorHistory.length - this.diagnosticsErrorHistoryLimit
2038
+ );
2039
+ }
2040
+ }
2041
+ collectFetchTransportDiagnostics(ctx) {
2042
+ const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
2043
+ const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
2044
+ const states = Array.from(this.fetchClientDiagnostics.values()).sort(
2045
+ (a, b) => a.fetchId.localeCompare(b.fetchId)
2046
+ );
2047
+ const summary = {
2048
+ detailLevel,
2049
+ totalClients: states.length,
2050
+ connectedClients: states.filter((state) => state.connected).length,
2051
+ destroyedClients: states.filter((state) => state.destroyed).length,
2052
+ delegationRequests: states.reduce(
2053
+ (acc, state) => acc + state.delegationRequests,
2054
+ 0
2055
+ ),
2056
+ delegationFailures: states.reduce(
2057
+ (acc, state) => acc + state.delegationFailures,
2058
+ 0
2059
+ ),
2060
+ signalTransmissions: states.reduce(
2061
+ (acc, state) => acc + state.signalTransmissions,
2062
+ 0
2063
+ ),
2064
+ signalFailures: states.reduce((acc, state) => acc + state.signalFailures, 0),
2065
+ statusChecks: states.reduce((acc, state) => acc + state.statusChecks, 0),
2066
+ statusFailures: states.reduce((acc, state) => acc + state.statusFailures, 0),
2067
+ latestError: states.slice().sort((a, b) => b.lastErrorAt - a.lastErrorAt).find((state) => state.lastError)?.lastError ?? null
2068
+ };
2069
+ if (detailLevel === "summary") {
2070
+ return {
2071
+ transportDiagnostics: {
2072
+ [serviceName]: {
2073
+ fetchClient: summary
2074
+ }
2075
+ }
2076
+ };
2077
+ }
2078
+ const clients = states.map((state) => {
2079
+ const details = {
2080
+ fetchId: state.fetchId,
2081
+ serviceName: state.serviceName,
2082
+ url: state.url,
2083
+ connected: state.connected,
2084
+ destroyed: state.destroyed,
2085
+ lastHandshakeAt: state.lastHandshakeAt,
2086
+ lastHandshakeError: state.lastHandshakeError,
2087
+ latestError: state.lastError,
2088
+ delegationRequests: state.delegationRequests,
2089
+ delegationFailures: state.delegationFailures,
2090
+ signalTransmissions: state.signalTransmissions,
2091
+ signalFailures: state.signalFailures,
2092
+ statusChecks: state.statusChecks,
2093
+ statusFailures: state.statusFailures
2094
+ };
2095
+ if (includeErrorHistory) {
2096
+ details.errorHistory = state.errorHistory.slice(-errorHistoryLimit);
2097
+ }
2098
+ return details;
2099
+ });
2100
+ return {
2101
+ transportDiagnostics: {
2102
+ [serviceName]: {
2103
+ fetchClient: {
2104
+ ...summary,
2105
+ clients
2106
+ }
2107
+ }
2108
+ }
2109
+ };
2110
+ }
1617
2111
  };
1618
2112
 
1619
2113
  // src/network/SocketController.ts
@@ -1661,10 +2155,6 @@ var waitForSocketConnection = async (socket, timeoutMs, createError) => {
1661
2155
 
1662
2156
  // src/network/SocketController.ts
1663
2157
  var SocketController = class _SocketController {
1664
- static get instance() {
1665
- if (!this._instance) this._instance = new _SocketController();
1666
- return this._instance;
1667
- }
1668
2158
  /**
1669
2159
  * Constructs the `SocketServer`, setting up a WebSocket server with specific configurations,
1670
2160
  * including connection state recovery, rate limiting, CORS handling, and custom event handling.
@@ -1683,6 +2173,13 @@ var SocketController = class _SocketController {
1683
2173
  * Initializes the `SocketServer` to be ready for WebSocket communication.
1684
2174
  */
1685
2175
  constructor() {
2176
+ this.socketClientDiagnostics = /* @__PURE__ */ new Map();
2177
+ this.diagnosticsErrorHistoryLimit = 100;
2178
+ CadenzaService.createMetaTask(
2179
+ "Collect socket transport diagnostics",
2180
+ (ctx) => this.collectSocketTransportDiagnostics(ctx),
2181
+ "Responds to distributed transport diagnostics inquiries with socket client data."
2182
+ ).respondsTo(META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT);
1686
2183
  CadenzaService.createMetaRoutine(
1687
2184
  "SocketServer",
1688
2185
  [
@@ -1910,6 +2407,13 @@ var SocketController = class _SocketController {
1910
2407
  const port = protocol === "https" ? 443 : servicePort;
1911
2408
  const URL = `${socketProtocol}://${serviceAddress}:${port}`;
1912
2409
  const fetchId = `${serviceAddress}_${port}`;
2410
+ const socketDiagnostics = this.ensureSocketClientDiagnostics(
2411
+ fetchId,
2412
+ serviceName,
2413
+ URL
2414
+ );
2415
+ socketDiagnostics.destroyed = false;
2416
+ socketDiagnostics.updatedAt = Date.now();
1913
2417
  let handshake = false;
1914
2418
  let errorCount = 0;
1915
2419
  const ERROR_LIMIT = 5;
@@ -1919,6 +2423,11 @@ var SocketController = class _SocketController {
1919
2423
  }
1920
2424
  const pendingDelegationIds = /* @__PURE__ */ new Set();
1921
2425
  const pendingTimers = /* @__PURE__ */ new Set();
2426
+ const syncPendingCounts = () => {
2427
+ socketDiagnostics.pendingDelegations = pendingDelegationIds.size;
2428
+ socketDiagnostics.pendingTimers = pendingTimers.size;
2429
+ socketDiagnostics.updatedAt = Date.now();
2430
+ };
1922
2431
  let handshakeTask = null;
1923
2432
  let emitWhenReady = null;
1924
2433
  let transmitTask = null;
@@ -1968,6 +2477,12 @@ var SocketController = class _SocketController {
1968
2477
  { socketId: socket?.id, serviceName, URL, event },
1969
2478
  "error"
1970
2479
  );
2480
+ this.recordSocketClientError(
2481
+ fetchId,
2482
+ serviceName,
2483
+ URL,
2484
+ waitResult.error
2485
+ );
1971
2486
  resolveWithError(waitResult.error);
1972
2487
  return;
1973
2488
  }
@@ -1976,6 +2491,7 @@ var SocketController = class _SocketController {
1976
2491
  timer = setTimeout(() => {
1977
2492
  if (timer) {
1978
2493
  pendingTimers.delete(timer);
2494
+ syncPendingCounts();
1979
2495
  timer = null;
1980
2496
  }
1981
2497
  CadenzaService.log(
@@ -1983,9 +2499,16 @@ var SocketController = class _SocketController {
1983
2499
  { socketId: socket?.id, serviceName, URL },
1984
2500
  "error"
1985
2501
  );
2502
+ this.recordSocketClientError(
2503
+ fetchId,
2504
+ serviceName,
2505
+ URL,
2506
+ `Socket event '${event}' timed out`
2507
+ );
1986
2508
  resolveWithError(`Socket event '${event}' timed out`);
1987
2509
  }, timeoutMs + 10);
1988
2510
  pendingTimers.add(timer);
2511
+ syncPendingCounts();
1989
2512
  }
1990
2513
  const connectedSocket = socket;
1991
2514
  if (!connectedSocket) {
@@ -1998,6 +2521,7 @@ var SocketController = class _SocketController {
1998
2521
  if (timer) {
1999
2522
  clearTimeout(timer);
2000
2523
  pendingTimers.delete(timer);
2524
+ syncPendingCounts();
2001
2525
  timer = null;
2002
2526
  }
2003
2527
  if (err) {
@@ -2011,6 +2535,12 @@ var SocketController = class _SocketController {
2011
2535
  },
2012
2536
  "warning"
2013
2537
  );
2538
+ this.recordSocketClientError(
2539
+ fetchId,
2540
+ serviceName,
2541
+ URL,
2542
+ err
2543
+ );
2014
2544
  response = {
2015
2545
  __error: `Timeout error: ${err}`,
2016
2546
  errored: true,
@@ -2027,6 +2557,10 @@ var SocketController = class _SocketController {
2027
2557
  };
2028
2558
  socket.on("connect", () => {
2029
2559
  if (handshake) return;
2560
+ socketDiagnostics.connected = true;
2561
+ socketDiagnostics.destroyed = false;
2562
+ socketDiagnostics.socketId = socket?.id ?? null;
2563
+ socketDiagnostics.updatedAt = Date.now();
2030
2564
  CadenzaService.emit(`meta.socket_client.connected:${fetchId}`, ctx);
2031
2565
  });
2032
2566
  socket.on("delegation_progress", (ctx2) => {
@@ -2045,6 +2579,12 @@ var SocketController = class _SocketController {
2045
2579
  });
2046
2580
  socket.on("connect_error", (err) => {
2047
2581
  handshake = false;
2582
+ socketDiagnostics.connected = false;
2583
+ socketDiagnostics.handshake = false;
2584
+ socketDiagnostics.connectErrors++;
2585
+ socketDiagnostics.lastHandshakeError = err.message;
2586
+ socketDiagnostics.updatedAt = Date.now();
2587
+ this.recordSocketClientError(fetchId, serviceName, URL, err);
2048
2588
  CadenzaService.log(
2049
2589
  "Socket connect error",
2050
2590
  { error: err.message, serviceName, socketId: socket?.id, URL },
@@ -2053,9 +2593,16 @@ var SocketController = class _SocketController {
2053
2593
  CadenzaService.emit(`meta.socket_client.connect_error:${fetchId}`, err);
2054
2594
  });
2055
2595
  socket.on("reconnect_attempt", (attempt) => {
2596
+ socketDiagnostics.reconnectAttempts = Math.max(
2597
+ socketDiagnostics.reconnectAttempts,
2598
+ attempt
2599
+ );
2600
+ socketDiagnostics.updatedAt = Date.now();
2056
2601
  CadenzaService.log(`Reconnect attempt: ${attempt}`);
2057
2602
  });
2058
2603
  socket.on("reconnect", (attempt) => {
2604
+ socketDiagnostics.connected = true;
2605
+ socketDiagnostics.updatedAt = Date.now();
2059
2606
  CadenzaService.log(`Socket reconnected after ${attempt} tries`, {
2060
2607
  socketId: socket?.id,
2061
2608
  URL,
@@ -2064,6 +2611,12 @@ var SocketController = class _SocketController {
2064
2611
  });
2065
2612
  socket.on("reconnect_error", (err) => {
2066
2613
  handshake = false;
2614
+ socketDiagnostics.connected = false;
2615
+ socketDiagnostics.handshake = false;
2616
+ socketDiagnostics.reconnectErrors++;
2617
+ socketDiagnostics.lastHandshakeError = err.message;
2618
+ socketDiagnostics.updatedAt = Date.now();
2619
+ this.recordSocketClientError(fetchId, serviceName, URL, err);
2067
2620
  CadenzaService.log(
2068
2621
  "Socket reconnect failed.",
2069
2622
  { error: err.message, serviceName, URL, socketId: socket?.id },
@@ -2072,6 +2625,9 @@ var SocketController = class _SocketController {
2072
2625
  });
2073
2626
  socket.on("error", (err) => {
2074
2627
  errorCount++;
2628
+ socketDiagnostics.socketErrors++;
2629
+ socketDiagnostics.updatedAt = Date.now();
2630
+ this.recordSocketClientError(fetchId, serviceName, URL, err);
2075
2631
  CadenzaService.log(
2076
2632
  "Socket error",
2077
2633
  { error: err, socketId: socket?.id, URL, serviceName },
@@ -2080,6 +2636,11 @@ var SocketController = class _SocketController {
2080
2636
  CadenzaService.emit("meta.socket_client.error", err);
2081
2637
  });
2082
2638
  socket.on("disconnect", () => {
2639
+ const disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
2640
+ socketDiagnostics.connected = false;
2641
+ socketDiagnostics.handshake = false;
2642
+ socketDiagnostics.lastDisconnectAt = disconnectedAt;
2643
+ socketDiagnostics.updatedAt = Date.now();
2083
2644
  CadenzaService.log(
2084
2645
  "Socket disconnected.",
2085
2646
  { URL, serviceName, socketId: socket?.id },
@@ -2098,6 +2659,8 @@ var SocketController = class _SocketController {
2098
2659
  async (ctx2, emit) => {
2099
2660
  if (handshake) return;
2100
2661
  handshake = true;
2662
+ socketDiagnostics.handshake = true;
2663
+ socketDiagnostics.updatedAt = Date.now();
2101
2664
  await emitWhenReady?.(
2102
2665
  "handshake",
2103
2666
  {
@@ -2109,6 +2672,11 @@ var SocketController = class _SocketController {
2109
2672
  1e4,
2110
2673
  (result) => {
2111
2674
  if (result.status === "success") {
2675
+ socketDiagnostics.connected = true;
2676
+ socketDiagnostics.handshake = true;
2677
+ socketDiagnostics.lastHandshakeAt = (/* @__PURE__ */ new Date()).toISOString();
2678
+ socketDiagnostics.lastHandshakeError = null;
2679
+ socketDiagnostics.updatedAt = Date.now();
2112
2680
  CadenzaService.log("Socket client connected", {
2113
2681
  result,
2114
2682
  serviceName,
@@ -2116,6 +2684,16 @@ var SocketController = class _SocketController {
2116
2684
  URL
2117
2685
  });
2118
2686
  } else {
2687
+ socketDiagnostics.connected = false;
2688
+ socketDiagnostics.handshake = false;
2689
+ socketDiagnostics.lastHandshakeError = result?.__error ?? result?.error ?? "Socket handshake failed";
2690
+ socketDiagnostics.updatedAt = Date.now();
2691
+ this.recordSocketClientError(
2692
+ fetchId,
2693
+ serviceName,
2694
+ URL,
2695
+ socketDiagnostics.lastHandshakeError
2696
+ );
2119
2697
  CadenzaService.log(
2120
2698
  "Socket handshake failed",
2121
2699
  { result, serviceName, socketId: socket?.id, URL },
@@ -2138,6 +2716,7 @@ var SocketController = class _SocketController {
2138
2716
  delete ctx2.__broadcast;
2139
2717
  const requestSentAt = Date.now();
2140
2718
  pendingDelegationIds.add(ctx2.__metadata.__deputyExecId);
2719
+ syncPendingCounts();
2141
2720
  emitWhenReady?.(
2142
2721
  "delegation",
2143
2722
  ctx2,
@@ -2155,6 +2734,15 @@ var SocketController = class _SocketController {
2155
2734
  }
2156
2735
  );
2157
2736
  pendingDelegationIds.delete(ctx2.__metadata.__deputyExecId);
2737
+ syncPendingCounts();
2738
+ if (resultContext?.errored || resultContext?.failed) {
2739
+ this.recordSocketClientError(
2740
+ fetchId,
2741
+ serviceName,
2742
+ URL,
2743
+ resultContext?.__error ?? resultContext?.error ?? "Socket delegation failed"
2744
+ );
2745
+ }
2158
2746
  resolve(resultContext);
2159
2747
  }
2160
2748
  );
@@ -2190,6 +2778,10 @@ var SocketController = class _SocketController {
2190
2778
  `Shutdown SocketClient ${URL}`,
2191
2779
  (ctx2, emit) => {
2192
2780
  handshake = false;
2781
+ socketDiagnostics.connected = false;
2782
+ socketDiagnostics.handshake = false;
2783
+ socketDiagnostics.destroyed = true;
2784
+ socketDiagnostics.updatedAt = Date.now();
2193
2785
  CadenzaService.log("Shutting down socket client", { URL, serviceName });
2194
2786
  socket?.close();
2195
2787
  handshakeTask?.destroy();
@@ -2219,10 +2811,12 @@ var SocketController = class _SocketController {
2219
2811
  });
2220
2812
  }
2221
2813
  pendingDelegationIds.clear();
2814
+ syncPendingCounts();
2222
2815
  for (const timer of pendingTimers) {
2223
2816
  clearTimeout(timer);
2224
2817
  }
2225
2818
  pendingTimers.clear();
2819
+ syncPendingCounts();
2226
2820
  },
2227
2821
  "Shuts down the socket client"
2228
2822
  ).doOn(
@@ -2236,6 +2830,157 @@ var SocketController = class _SocketController {
2236
2830
  "Connects to a specified socket server"
2237
2831
  ).doOn("meta.fetch.handshake_complete").emitsOnFail("meta.socket_client.connect_failed");
2238
2832
  }
2833
+ static get instance() {
2834
+ if (!this._instance) this._instance = new _SocketController();
2835
+ return this._instance;
2836
+ }
2837
+ resolveTransportDiagnosticsOptions(ctx) {
2838
+ const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
2839
+ const includeErrorHistory = Boolean(ctx.includeErrorHistory);
2840
+ const requestedLimit = Number(ctx.errorHistoryLimit);
2841
+ const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
2842
+ return {
2843
+ detailLevel,
2844
+ includeErrorHistory,
2845
+ errorHistoryLimit
2846
+ };
2847
+ }
2848
+ ensureSocketClientDiagnostics(fetchId, serviceName, url) {
2849
+ let state = this.socketClientDiagnostics.get(fetchId);
2850
+ if (!state) {
2851
+ state = {
2852
+ fetchId,
2853
+ serviceName,
2854
+ url,
2855
+ socketId: null,
2856
+ connected: false,
2857
+ handshake: false,
2858
+ reconnectAttempts: 0,
2859
+ connectErrors: 0,
2860
+ reconnectErrors: 0,
2861
+ socketErrors: 0,
2862
+ pendingDelegations: 0,
2863
+ pendingTimers: 0,
2864
+ destroyed: false,
2865
+ lastHandshakeAt: null,
2866
+ lastHandshakeError: null,
2867
+ lastDisconnectAt: null,
2868
+ lastError: null,
2869
+ lastErrorAt: 0,
2870
+ errorHistory: [],
2871
+ updatedAt: Date.now()
2872
+ };
2873
+ this.socketClientDiagnostics.set(fetchId, state);
2874
+ } else {
2875
+ state.serviceName = serviceName;
2876
+ state.url = url;
2877
+ }
2878
+ return state;
2879
+ }
2880
+ getErrorMessage(error) {
2881
+ if (error instanceof Error) {
2882
+ return error.message;
2883
+ }
2884
+ if (typeof error === "string") {
2885
+ return error;
2886
+ }
2887
+ try {
2888
+ return JSON.stringify(error);
2889
+ } catch {
2890
+ return String(error);
2891
+ }
2892
+ }
2893
+ recordSocketClientError(fetchId, serviceName, url, error) {
2894
+ const state = this.ensureSocketClientDiagnostics(fetchId, serviceName, url);
2895
+ const message = this.getErrorMessage(error);
2896
+ const now = Date.now();
2897
+ state.lastError = message;
2898
+ state.lastErrorAt = now;
2899
+ state.updatedAt = now;
2900
+ state.errorHistory.push({
2901
+ at: new Date(now).toISOString(),
2902
+ message
2903
+ });
2904
+ if (state.errorHistory.length > this.diagnosticsErrorHistoryLimit) {
2905
+ state.errorHistory.splice(
2906
+ 0,
2907
+ state.errorHistory.length - this.diagnosticsErrorHistoryLimit
2908
+ );
2909
+ }
2910
+ }
2911
+ collectSocketTransportDiagnostics(ctx) {
2912
+ const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
2913
+ const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
2914
+ const states = Array.from(this.socketClientDiagnostics.values()).sort(
2915
+ (a, b) => a.fetchId.localeCompare(b.fetchId)
2916
+ );
2917
+ const summary = {
2918
+ detailLevel,
2919
+ totalClients: states.length,
2920
+ connectedClients: states.filter((state) => state.connected).length,
2921
+ activeHandshakes: states.filter((state) => state.handshake).length,
2922
+ pendingDelegations: states.reduce(
2923
+ (acc, state) => acc + state.pendingDelegations,
2924
+ 0
2925
+ ),
2926
+ pendingTimers: states.reduce((acc, state) => acc + state.pendingTimers, 0),
2927
+ reconnectAttempts: states.reduce(
2928
+ (acc, state) => acc + state.reconnectAttempts,
2929
+ 0
2930
+ ),
2931
+ connectErrors: states.reduce((acc, state) => acc + state.connectErrors, 0),
2932
+ reconnectErrors: states.reduce(
2933
+ (acc, state) => acc + state.reconnectErrors,
2934
+ 0
2935
+ ),
2936
+ socketErrors: states.reduce((acc, state) => acc + state.socketErrors, 0),
2937
+ latestError: states.slice().sort((a, b) => b.lastErrorAt - a.lastErrorAt).find((state) => state.lastError)?.lastError ?? null
2938
+ };
2939
+ if (detailLevel === "summary") {
2940
+ return {
2941
+ transportDiagnostics: {
2942
+ [serviceName]: {
2943
+ socketClient: summary
2944
+ }
2945
+ }
2946
+ };
2947
+ }
2948
+ const clients = states.map((state) => {
2949
+ const details = {
2950
+ fetchId: state.fetchId,
2951
+ serviceName: state.serviceName,
2952
+ url: state.url,
2953
+ socketId: state.socketId,
2954
+ connected: state.connected,
2955
+ handshake: state.handshake,
2956
+ reconnectAttempts: state.reconnectAttempts,
2957
+ connectErrors: state.connectErrors,
2958
+ reconnectErrors: state.reconnectErrors,
2959
+ socketErrors: state.socketErrors,
2960
+ pendingDelegations: state.pendingDelegations,
2961
+ pendingTimers: state.pendingTimers,
2962
+ destroyed: state.destroyed,
2963
+ lastHandshakeAt: state.lastHandshakeAt,
2964
+ lastHandshakeError: state.lastHandshakeError,
2965
+ lastDisconnectAt: state.lastDisconnectAt,
2966
+ latestError: state.lastError
2967
+ };
2968
+ if (includeErrorHistory) {
2969
+ details.errorHistory = state.errorHistory.slice(-errorHistoryLimit);
2970
+ }
2971
+ return details;
2972
+ });
2973
+ return {
2974
+ transportDiagnostics: {
2975
+ [serviceName]: {
2976
+ socketClient: {
2977
+ ...summary,
2978
+ clients
2979
+ }
2980
+ }
2981
+ }
2982
+ };
2983
+ }
2239
2984
  };
2240
2985
 
2241
2986
  // src/utils/tools.ts
@@ -2339,25 +3084,8 @@ var GraphMetadataController = class _GraphMetadataController {
2339
3084
  return {
2340
3085
  data: {
2341
3086
  ...ctx.data,
2342
- serviceName: CadenzaService.serviceRegistry.serviceName,
2343
- inputContextSchemaId: ctx.data.inputContextSchemaId ? {
2344
- subOperation: "insert",
2345
- table: "context_schema",
2346
- data: {
2347
- ...ctx.data.inputContextSchemaId
2348
- },
2349
- return: "uuid"
2350
- } : null,
2351
- outputContextSchemaId: ctx.data.outputContextSchemaId ? {
2352
- subOperation: "insert",
2353
- table: "context_schema",
2354
- data: {
2355
- ...ctx.data.outputContextSchemaId
2356
- },
2357
- return: "uuid"
2358
- } : null
2359
- },
2360
- transaction: true
3087
+ serviceName: CadenzaService.serviceRegistry.serviceName
3088
+ }
2361
3089
  };
2362
3090
  }).doOn("meta.task.created").emits("global.meta.graph_metadata.task_created");
2363
3091
  CadenzaService.createMetaTask("Handle task update", (ctx) => {
@@ -2564,9 +3292,11 @@ var GraphMetadataController = class _GraphMetadataController {
2564
3292
  { concurrency: 100, isSubMeta: true }
2565
3293
  ).doOn("meta.node.mapped", "meta.node.detected_previous_task_execution").emits("global.meta.graph_metadata.relationship_executed");
2566
3294
  CadenzaService.createMetaTask("Handle Intent Creation", (ctx) => {
3295
+ const intentName = ctx.data?.name;
2567
3296
  return {
2568
3297
  data: {
2569
- ...ctx.data
3298
+ ...ctx.data,
3299
+ isMeta: intentName ? isMetaIntentName(intentName) : false
2570
3300
  }
2571
3301
  };
2572
3302
  }).doOn("meta.inquiry_broker.added").emits("global.meta.graph_metadata.intent_created");
@@ -2598,6 +3328,52 @@ var SCHEMA_TYPES = [
2598
3328
  // src/database/DatabaseController.ts
2599
3329
  import { Pool } from "pg";
2600
3330
  import { camelCase, snakeCase } from "lodash-es";
3331
+ function resolveTableQueryIntents(serviceName, tableName, table, defaultInputSchema) {
3332
+ const resolvedServiceName = serviceName ?? "unknown-service";
3333
+ const defaultIntentName = `query-${resolvedServiceName}-${tableName}`;
3334
+ const defaultDescription = `Perform a query operation on the ${tableName} table`;
3335
+ const intents = [
3336
+ {
3337
+ name: defaultIntentName,
3338
+ description: defaultDescription,
3339
+ input: defaultInputSchema
3340
+ }
3341
+ ];
3342
+ const warnings = [];
3343
+ const names = /* @__PURE__ */ new Set([defaultIntentName]);
3344
+ for (const customIntent of table.customIntents?.query ?? []) {
3345
+ const name = typeof customIntent === "string" ? customIntent.trim() : customIntent.intent?.trim();
3346
+ if (!name) {
3347
+ warnings.push(`Skipped empty custom query intent for table '${tableName}'.`);
3348
+ continue;
3349
+ }
3350
+ if (name.length > 100) {
3351
+ warnings.push(
3352
+ `Skipped custom query intent '${name}' for table '${tableName}': name must be <= 100 characters.`
3353
+ );
3354
+ continue;
3355
+ }
3356
+ if (name.includes(" ") || name.includes(".") || name.includes("\\")) {
3357
+ warnings.push(
3358
+ `Skipped custom query intent '${name}' for table '${tableName}': name cannot contain spaces, dots or backslashes.`
3359
+ );
3360
+ continue;
3361
+ }
3362
+ if (names.has(name)) {
3363
+ warnings.push(
3364
+ `Skipped duplicate custom query intent '${name}' for table '${tableName}'.`
3365
+ );
3366
+ continue;
3367
+ }
3368
+ names.add(name);
3369
+ intents.push({
3370
+ name,
3371
+ description: typeof customIntent === "string" ? `Perform a query operation on the ${tableName} table` : customIntent.description ?? defaultDescription,
3372
+ input: typeof customIntent === "string" ? defaultInputSchema : customIntent.input ?? defaultInputSchema
3373
+ });
3374
+ }
3375
+ return { intents, warnings };
3376
+ }
2601
3377
  var DatabaseController = class _DatabaseController {
2602
3378
  /**
2603
3379
  * Constructor for initializing the `DatabaseService` class.
@@ -2739,6 +3515,20 @@ var DatabaseController = class _DatabaseController {
2739
3515
  }
2740
3516
  }
2741
3517
  }
3518
+ if (table.customIntents?.query) {
3519
+ if (!Array.isArray(table.customIntents.query)) {
3520
+ throw new Error(
3521
+ `Invalid customIntents.query for ${tableName}: expected array`
3522
+ );
3523
+ }
3524
+ for (const customIntent of table.customIntents.query) {
3525
+ if (typeof customIntent !== "string" && (typeof customIntent !== "object" || !customIntent || typeof customIntent.intent !== "string")) {
3526
+ throw new Error(
3527
+ `Invalid custom query intent on ${tableName}: expected string or object with intent`
3528
+ );
3529
+ }
3530
+ }
3531
+ }
2742
3532
  }
2743
3533
  }
2744
3534
  console.log("SCHEMA VALIDATED");
@@ -3775,13 +4565,30 @@ var DatabaseController = class _DatabaseController {
3775
4565
  }) ?? []
3776
4566
  );
3777
4567
  if (op === "query") {
3778
- const intentName = `query-${CadenzaService.serviceRegistry.serviceName}-${tableName}`;
3779
- CadenzaService.defineIntent({
3780
- name: intentName,
3781
- description: `Perform a query operation on the ${tableName} table`,
3782
- input: schema
3783
- });
3784
- task.respondsTo(intentName);
4568
+ const { intents, warnings } = resolveTableQueryIntents(
4569
+ CadenzaService.serviceRegistry?.serviceName,
4570
+ tableName,
4571
+ table,
4572
+ schema
4573
+ );
4574
+ for (const warning of warnings) {
4575
+ CadenzaService.log(
4576
+ "Skipped custom query intent registration.",
4577
+ {
4578
+ tableName,
4579
+ warning
4580
+ },
4581
+ "warning"
4582
+ );
4583
+ }
4584
+ for (const intent of intents) {
4585
+ CadenzaService.defineIntent({
4586
+ name: intent.name,
4587
+ description: intent.description,
4588
+ input: intent.input
4589
+ });
4590
+ }
4591
+ task.respondsTo(...intents.map((intent) => intent.name));
3785
4592
  }
3786
4593
  }
3787
4594
  getInputSchema(op, tableName, table) {
@@ -3873,18 +4680,18 @@ function getInsertDataSchemaFromTable(table, tableName) {
3873
4680
  Object.entries(table.fields).map((field) => {
3874
4681
  return [
3875
4682
  field[0],
3876
- [
3877
- {
4683
+ {
4684
+ value: {
3878
4685
  type: tableFieldTypeToSchemaType(field[1].type),
3879
4686
  description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
3880
4687
  },
3881
- {
4688
+ effect: {
3882
4689
  type: "string",
3883
4690
  constraints: {
3884
4691
  oneOf: ["increment", "decrement", "set"]
3885
4692
  }
3886
4693
  },
3887
- {
4694
+ subOperation: {
3888
4695
  type: "object",
3889
4696
  properties: {
3890
4697
  subOperation: {
@@ -3894,17 +4701,17 @@ function getInsertDataSchemaFromTable(table, tableName) {
3894
4701
  table: {
3895
4702
  type: "string"
3896
4703
  },
3897
- data: [
3898
- {
4704
+ data: {
4705
+ single: {
3899
4706
  type: "object"
3900
4707
  },
3901
- {
4708
+ batch: {
3902
4709
  type: "array",
3903
4710
  items: {
3904
4711
  type: "object"
3905
4712
  }
3906
4713
  }
3907
- ],
4714
+ },
3908
4715
  filter: {
3909
4716
  type: "object"
3910
4717
  },
@@ -3920,7 +4727,7 @@ function getInsertDataSchemaFromTable(table, tableName) {
3920
4727
  },
3921
4728
  required: ["subOperation", "table"]
3922
4729
  }
3923
- ]
4730
+ }
3924
4731
  ];
3925
4732
  })
3926
4733
  )
@@ -3928,13 +4735,13 @@ function getInsertDataSchemaFromTable(table, tableName) {
3928
4735
  required: Object.entries(table.fields).filter((field) => field[1].required || field[1].primary).map((field) => field[0]),
3929
4736
  strict: true
3930
4737
  };
3931
- return [
3932
- dataSchema,
3933
- {
4738
+ return {
4739
+ single: dataSchema,
4740
+ batch: {
3934
4741
  type: "array",
3935
4742
  items: dataSchema
3936
4743
  }
3937
- ];
4744
+ };
3938
4745
  }
3939
4746
  function getQueryFilterSchemaFromTable(table, tableName) {
3940
4747
  return {
@@ -3944,24 +4751,24 @@ function getQueryFilterSchemaFromTable(table, tableName) {
3944
4751
  Object.entries(table.fields).map((field) => {
3945
4752
  return [
3946
4753
  field[0],
3947
- [
3948
- {
4754
+ {
4755
+ value: {
3949
4756
  type: tableFieldTypeToSchemaType(field[1].type),
3950
4757
  description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
3951
4758
  },
3952
- {
4759
+ in: {
3953
4760
  type: "array",
3954
4761
  items: {
3955
4762
  type: tableFieldTypeToSchemaType(field[1].type)
3956
4763
  }
3957
4764
  }
3958
- ]
4765
+ }
3959
4766
  ];
3960
4767
  })
3961
4768
  )
3962
4769
  },
3963
4770
  strict: true,
3964
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4771
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
3965
4772
  };
3966
4773
  }
3967
4774
  function getQueryFieldsSchemaFromTable(table, tableName) {
@@ -3973,7 +4780,7 @@ function getQueryFieldsSchemaFromTable(table, tableName) {
3973
4780
  oneOf: Object.keys(table.fields)
3974
4781
  }
3975
4782
  },
3976
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4783
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
3977
4784
  };
3978
4785
  }
3979
4786
  function getQueryJoinsSchemaFromTable(table, tableName) {
@@ -4020,7 +4827,7 @@ function getQueryJoinsSchemaFromTable(table, tableName) {
4020
4827
  )
4021
4828
  },
4022
4829
  strict: true,
4023
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4830
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
4024
4831
  };
4025
4832
  }
4026
4833
  function getQuerySortSchemaFromTable(table, tableName) {
@@ -4042,7 +4849,7 @@ function getQuerySortSchemaFromTable(table, tableName) {
4042
4849
  )
4043
4850
  },
4044
4851
  strict: true,
4045
- description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry.serviceName}.`
4852
+ description: `Inferred from table '${tableName}' on database service ${CadenzaService.serviceRegistry?.serviceName ?? "unknown-service"}.`
4046
4853
  };
4047
4854
  }
4048
4855
  function getQueryLimitSchemaFromTable() {
@@ -4104,12 +4911,10 @@ function getQueryOnConflictSchemaFromTable(table, tableName) {
4104
4911
  Object.entries(table.fields).map((field) => {
4105
4912
  return [
4106
4913
  field[0],
4107
- [
4108
- {
4109
- type: tableFieldTypeToSchemaType(field[1].type),
4110
- description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
4111
- }
4112
- ]
4914
+ {
4915
+ type: tableFieldTypeToSchemaType(field[1].type),
4916
+ description: `Inferred from field '${field[0]}' of type [${field[1].type}] on table ${tableName}.`
4917
+ }
4113
4918
  ];
4114
4919
  })
4115
4920
  )
@@ -4467,6 +5272,75 @@ var GraphSyncController = class _GraphSyncController {
4467
5272
  { concurrency: 30 }
4468
5273
  ) : CadenzaService.get("dbInsertSignalToTaskMap"))?.then(registerSignalTask)
4469
5274
  );
5275
+ const registerIntentTask = CadenzaService.createMetaTask(
5276
+ "Record intent registration",
5277
+ (ctx) => {
5278
+ if (!ctx.__syncing) {
5279
+ return;
5280
+ }
5281
+ CadenzaService.debounce("meta.sync_controller.synced_resource", {
5282
+ delayMs: 3e3
5283
+ });
5284
+ const task = CadenzaService.get(ctx.__taskName);
5285
+ task.__registeredIntents = task.__registeredIntents ?? /* @__PURE__ */ new Set();
5286
+ task.__registeredIntents.add(ctx.__intent);
5287
+ }
5288
+ );
5289
+ this.registerIntentToTaskMapTask = CadenzaService.createMetaTask(
5290
+ "Split intents of task",
5291
+ function* (ctx) {
5292
+ const task = ctx.task;
5293
+ if (task.hidden || !task.register) return;
5294
+ task.__registeredIntents = task.__registeredIntents ?? /* @__PURE__ */ new Set();
5295
+ task.__invalidMetaIntentWarnings = task.__invalidMetaIntentWarnings ?? /* @__PURE__ */ new Set();
5296
+ for (const intent of task.handlesIntents) {
5297
+ if (task.__registeredIntents.has(intent)) continue;
5298
+ if (isMetaIntentName(intent) && !task.isMeta) {
5299
+ if (!task.__invalidMetaIntentWarnings.has(intent)) {
5300
+ task.__invalidMetaIntentWarnings.add(intent);
5301
+ CadenzaService.log(
5302
+ "Skipping intent-to-task registration: non-meta task cannot handle meta intent.",
5303
+ {
5304
+ intent,
5305
+ taskName: task.name,
5306
+ taskVersion: task.version
5307
+ },
5308
+ "warning"
5309
+ );
5310
+ }
5311
+ continue;
5312
+ }
5313
+ yield {
5314
+ data: {
5315
+ intentName: intent,
5316
+ taskName: task.name,
5317
+ taskVersion: task.version,
5318
+ serviceName: CadenzaService.serviceRegistry.serviceName
5319
+ },
5320
+ __taskName: task.name,
5321
+ __intent: intent
5322
+ };
5323
+ }
5324
+ }
5325
+ ).then(
5326
+ (this.isCadenzaDBReady ? CadenzaService.createCadenzaDBInsertTask(
5327
+ "intent_to_task_map",
5328
+ {
5329
+ onConflict: {
5330
+ target: [
5331
+ "intent_name",
5332
+ "task_name",
5333
+ "task_version",
5334
+ "service_name"
5335
+ ],
5336
+ action: {
5337
+ do: "nothing"
5338
+ }
5339
+ }
5340
+ },
5341
+ { concurrency: 30 }
5342
+ ) : CadenzaService.get("dbInsertIntentToTaskMap"))?.then(registerIntentTask)
5343
+ );
4470
5344
  this.registerTaskMapTask = CadenzaService.createMetaTask(
4471
5345
  "Register task map to DB",
4472
5346
  function* (ctx) {
@@ -4589,6 +5463,7 @@ var GraphSyncController = class _GraphSyncController {
4589
5463
  CadenzaService.registry.doForEachTask.clone().doOn("meta.sync_controller.synced_tasks").then(
4590
5464
  this.registerTaskMapTask,
4591
5465
  this.registerSignalToTaskMapTask,
5466
+ this.registerIntentToTaskMapTask,
4592
5467
  this.registerDeputyRelationshipTask
4593
5468
  );
4594
5469
  CadenzaService.registry.getAllRoutines.clone().doOn("meta.sync_controller.synced_routines").then(this.splitTasksInRoutines);
@@ -4730,11 +5605,192 @@ var CadenzaService = class {
4730
5605
  Cadenza.interval(signal, context, intervalMs, leading, startDateTime);
4731
5606
  }
4732
5607
  static defineIntent(intent) {
4733
- this.inquiryBroker?.intents.set(intent.name, intent);
5608
+ this.inquiryBroker?.addIntent(intent);
4734
5609
  return intent;
4735
5610
  }
4736
- static async inquire(inquiry, context, options) {
4737
- return this.inquiryBroker?.inquire(inquiry, context, options);
5611
+ static getInquiryResponderDescriptor(task) {
5612
+ return this.serviceRegistry.getInquiryResponderDescriptor(task);
5613
+ }
5614
+ static compareInquiryResponders(left, right) {
5615
+ return compareResponderDescriptors(left.descriptor, right.descriptor);
5616
+ }
5617
+ static buildInquirySummary(inquiry, startedAt, statuses, totalResponders) {
5618
+ const counts = summarizeResponderStatuses(statuses);
5619
+ const isMetaInquiry = isMetaIntentName(inquiry);
5620
+ const eligibleResponders = statuses.length;
5621
+ return {
5622
+ inquiry,
5623
+ isMetaInquiry,
5624
+ totalResponders,
5625
+ eligibleResponders,
5626
+ filteredOutResponders: Math.max(0, totalResponders - eligibleResponders),
5627
+ responded: counts.responded,
5628
+ failed: counts.failed,
5629
+ timedOut: counts.timedOut,
5630
+ pending: counts.pending,
5631
+ durationMs: Date.now() - startedAt,
5632
+ responders: statuses
5633
+ };
5634
+ }
5635
+ static async inquire(inquiry, context, options = {}) {
5636
+ this.bootstrap();
5637
+ const observer = this.inquiryBroker?.inquiryObservers.get(inquiry);
5638
+ const allResponders = observer ? Array.from(observer.tasks).map((task) => ({
5639
+ task,
5640
+ descriptor: this.getInquiryResponderDescriptor(task)
5641
+ })) : [];
5642
+ const isMetaInquiry = isMetaIntentName(inquiry);
5643
+ const responders = allResponders.filter(({ task, descriptor }) => {
5644
+ const shouldExecute = shouldExecuteInquiryResponder(inquiry, task.isMeta);
5645
+ if (shouldExecute) {
5646
+ return true;
5647
+ }
5648
+ const warningKey = `${inquiry}|${descriptor.serviceName}|${descriptor.taskName}|${descriptor.taskVersion}|${descriptor.localTaskName}`;
5649
+ if (!this.warnedInvalidMetaIntentResponderKeys.has(warningKey)) {
5650
+ this.warnedInvalidMetaIntentResponderKeys.add(warningKey);
5651
+ this.log(
5652
+ "Skipping non-meta task for meta intent inquiry.",
5653
+ {
5654
+ inquiry,
5655
+ responder: descriptor
5656
+ },
5657
+ "warning",
5658
+ descriptor.serviceName
5659
+ );
5660
+ }
5661
+ return false;
5662
+ });
5663
+ if (responders.length === 0) {
5664
+ return {
5665
+ __inquiryMeta: {
5666
+ inquiry,
5667
+ isMetaInquiry,
5668
+ totalResponders: allResponders.length,
5669
+ eligibleResponders: 0,
5670
+ filteredOutResponders: allResponders.length,
5671
+ responded: 0,
5672
+ failed: 0,
5673
+ timedOut: 0,
5674
+ pending: 0,
5675
+ durationMs: 0,
5676
+ responders: []
5677
+ }
5678
+ };
5679
+ }
5680
+ responders.sort(this.compareInquiryResponders.bind(this));
5681
+ const overallTimeoutMs = options.overallTimeoutMs ?? options.timeout ?? 0;
5682
+ const requireComplete = options.requireComplete ?? false;
5683
+ const perResponderTimeoutMs = options.perResponderTimeoutMs;
5684
+ const startedAt = Date.now();
5685
+ const statuses = [];
5686
+ const statusByTask = /* @__PURE__ */ new Map();
5687
+ for (const responder of responders) {
5688
+ const status = {
5689
+ ...responder.descriptor,
5690
+ status: "timed_out",
5691
+ durationMs: 0
5692
+ };
5693
+ statuses.push(status);
5694
+ statusByTask.set(responder.task, status);
5695
+ }
5696
+ const resultsByTask = /* @__PURE__ */ new Map();
5697
+ const resolverTasks = [];
5698
+ const pending = new Set(responders.map((r) => r.task));
5699
+ const startTimeByTask = /* @__PURE__ */ new Map();
5700
+ this.emit("meta.inquiry_broker.inquire", { inquiry, context });
5701
+ return new Promise((resolve, reject) => {
5702
+ let finalized = false;
5703
+ let timeoutId;
5704
+ const finalize = (timedOut) => {
5705
+ if (finalized) return;
5706
+ finalized = true;
5707
+ if (timeoutId) {
5708
+ clearTimeout(timeoutId);
5709
+ timeoutId = void 0;
5710
+ }
5711
+ for (const resolverTask of resolverTasks) {
5712
+ resolverTask.destroy();
5713
+ }
5714
+ if (timedOut && pending.size > 0) {
5715
+ for (const task of pending) {
5716
+ const status = statusByTask.get(task);
5717
+ if (!status) continue;
5718
+ status.status = "timed_out";
5719
+ status.durationMs = Date.now() - (startTimeByTask.get(task) ?? startedAt);
5720
+ }
5721
+ }
5722
+ const fulfilledContexts = responders.filter((responder) => resultsByTask.has(responder.task)).map((responder) => resultsByTask.get(responder.task));
5723
+ const mergedContext = mergeInquiryContexts(fulfilledContexts);
5724
+ const inquiryMeta = this.buildInquirySummary(
5725
+ inquiry,
5726
+ startedAt,
5727
+ statuses,
5728
+ allResponders.length
5729
+ );
5730
+ const responseContext = {
5731
+ ...mergedContext,
5732
+ __inquiryMeta: inquiryMeta
5733
+ };
5734
+ if (requireComplete && (timedOut || inquiryMeta.failed > 0 || inquiryMeta.timedOut > 0 || inquiryMeta.pending > 0)) {
5735
+ reject({
5736
+ ...responseContext,
5737
+ __error: `Inquiry '${inquiry}' did not complete successfully`,
5738
+ errored: true
5739
+ });
5740
+ return;
5741
+ }
5742
+ resolve(responseContext);
5743
+ };
5744
+ if (overallTimeoutMs > 0) {
5745
+ timeoutId = setTimeout(() => finalize(true), overallTimeoutMs);
5746
+ }
5747
+ for (const responder of responders) {
5748
+ const { task, descriptor } = responder;
5749
+ const inquiryId = uuid3();
5750
+ startTimeByTask.set(task, Date.now());
5751
+ const resolverTask = this.createEphemeralMetaTask(
5752
+ `Resolve inquiry ${inquiry} for ${descriptor.localTaskName}`,
5753
+ (resultCtx) => {
5754
+ if (finalized) {
5755
+ return;
5756
+ }
5757
+ pending.delete(task);
5758
+ const status = statusByTask.get(task);
5759
+ if (status) {
5760
+ status.durationMs = Date.now() - (startTimeByTask.get(task) ?? startedAt);
5761
+ if (resultCtx?.errored || resultCtx?.failed) {
5762
+ status.status = "failed";
5763
+ status.error = String(
5764
+ resultCtx?.__error ?? resultCtx?.error ?? "Inquiry responder failed"
5765
+ );
5766
+ } else {
5767
+ status.status = "fulfilled";
5768
+ resultsByTask.set(task, resultCtx);
5769
+ }
5770
+ }
5771
+ if (pending.size === 0) {
5772
+ finalize(false);
5773
+ }
5774
+ },
5775
+ "Resolves distributed inquiry responder result",
5776
+ { register: false }
5777
+ ).doOn(`meta.node.graph_completed:${inquiryId}`);
5778
+ resolverTasks.push(resolverTask);
5779
+ const executionContext = {
5780
+ ...context,
5781
+ __routineExecId: inquiryId,
5782
+ __isInquiry: true
5783
+ };
5784
+ if (perResponderTimeoutMs !== void 0) {
5785
+ executionContext.__timeout = perResponderTimeoutMs;
5786
+ }
5787
+ if (task.isMeta) {
5788
+ this.metaRunner?.run(task, executionContext);
5789
+ } else {
5790
+ this.runner?.run(task, executionContext);
5791
+ }
5792
+ }
5793
+ });
4738
5794
  }
4739
5795
  /**
4740
5796
  * Executes the given task or graph routine within the provided context using the configured runner.
@@ -5771,6 +6827,7 @@ var CadenzaService = class {
5771
6827
  };
5772
6828
  CadenzaService.isBootstrapped = false;
5773
6829
  CadenzaService.serviceCreated = false;
6830
+ CadenzaService.warnedInvalidMetaIntentResponderKeys = /* @__PURE__ */ new Set();
5774
6831
 
5775
6832
  // src/index.ts
5776
6833
  import {