@cadenza.io/core 3.25.0 → 3.26.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
@@ -1240,6 +1240,7 @@ var SignalBroker = class _SignalBroker {
1240
1240
  // TODO: Signals should be a class with a the observers, registered flag and other data.
1241
1241
  this.signalObservers = /* @__PURE__ */ new Map();
1242
1242
  this.emittedSignalsRegistry = /* @__PURE__ */ new Set();
1243
+ this.signalMetadataRegistry = /* @__PURE__ */ new Map();
1243
1244
  // ── Flush Strategy Management ───────────────────────────────────────
1244
1245
  this.flushStrategies = /* @__PURE__ */ new Map();
1245
1246
  this.strategyData = /* @__PURE__ */ new Map();
@@ -1272,6 +1273,47 @@ var SignalBroker = class _SignalBroker {
1272
1273
  setVerbose(value) {
1273
1274
  this.verbose = value;
1274
1275
  }
1276
+ resolveSignalMetadataKey(signal) {
1277
+ const normalizedSignal = typeof signal === "string" ? signal.trim() : "";
1278
+ if (!normalizedSignal) {
1279
+ return "";
1280
+ }
1281
+ return normalizedSignal.split(":")[0] ?? normalizedSignal;
1282
+ }
1283
+ normalizeSignalMetadata(metadata) {
1284
+ if (!metadata) {
1285
+ return void 0;
1286
+ }
1287
+ const deliveryMode = metadata.deliveryMode === "broadcast" ? "broadcast" : "single";
1288
+ const broadcastFilter = metadata.broadcastFilter && typeof metadata.broadcastFilter === "object" && !Array.isArray(metadata.broadcastFilter) ? merge_default({}, metadata.broadcastFilter) : null;
1289
+ return {
1290
+ deliveryMode,
1291
+ broadcastFilter
1292
+ };
1293
+ }
1294
+ setSignalMetadata(signal, metadata) {
1295
+ const metadataKey = this.resolveSignalMetadataKey(signal);
1296
+ if (!metadataKey) {
1297
+ return;
1298
+ }
1299
+ const normalized = this.normalizeSignalMetadata(metadata);
1300
+ if (!normalized) {
1301
+ this.signalMetadataRegistry.delete(metadataKey);
1302
+ return;
1303
+ }
1304
+ this.signalMetadataRegistry.set(metadataKey, normalized);
1305
+ }
1306
+ getSignalMetadata(signal) {
1307
+ const metadataKey = this.resolveSignalMetadataKey(signal);
1308
+ if (!metadataKey) {
1309
+ return void 0;
1310
+ }
1311
+ const metadata = this.signalMetadataRegistry.get(metadataKey);
1312
+ if (!metadata) {
1313
+ return void 0;
1314
+ }
1315
+ return merge_default({}, metadata);
1316
+ }
1275
1317
  // Dor debugging
1276
1318
  logMemoryFootprint(label = "current") {
1277
1319
  console.log(`[${label}] SignalBroker state sizes:`);
@@ -1356,7 +1398,8 @@ var SignalBroker = class _SignalBroker {
1356
1398
  const processedSignals = uniqueSignals.map((signal) => ({
1357
1399
  signal,
1358
1400
  data: {
1359
- registered: this.signalObservers.get(signal)?.registered ?? false
1401
+ registered: this.signalObservers.get(signal)?.registered ?? false,
1402
+ metadata: this.getSignalMetadata(signal) ?? null
1360
1403
  }
1361
1404
  }));
1362
1405
  return {
@@ -1706,10 +1749,11 @@ var SignalBroker = class _SignalBroker {
1706
1749
  execute(signal, context) {
1707
1750
  const isMeta = signal.includes("meta.");
1708
1751
  const isSubMeta = signal.includes("sub_meta.") || context.__isSubMeta;
1752
+ const isReceivedSignalTransmission = context.__receivedSignalTransmission === true;
1709
1753
  const isMetric = context.__signalEmission?.isMetric;
1754
+ const isNewTrace = !context.__signalEmission?.executionTraceId && !context.__metadata?.__executionTraceId && !context.__executionTraceId;
1710
1755
  const executionTraceId = context.__signalEmission?.executionTraceId ?? context.__metadata?.__executionTraceId ?? context.__executionTraceId ?? uuid();
1711
1756
  if (!isSubMeta && (!isMeta || this.debug)) {
1712
- const isNewTrace = !context.__signalEmission?.executionTraceId && !context.__metadata?.__executionTraceId && !context.__executionTraceId;
1713
1757
  if (isNewTrace) {
1714
1758
  this.emit("sub_meta.signal_broker.new_trace", {
1715
1759
  data: {
@@ -1738,21 +1782,33 @@ var SignalBroker = class _SignalBroker {
1738
1782
  const signalParts = signal.split(":");
1739
1783
  const signalName = signalParts[0];
1740
1784
  const signalTag = signalParts.length > 1 ? signalParts[1] : null;
1785
+ const existingSignalEmission = context.__signalEmission && typeof context.__signalEmission === "object" ? context.__signalEmission : {};
1741
1786
  context.__signalEmission = {
1742
- ...context.__signalEmission ?? {},
1743
- uuid: uuid(),
1744
- executionTraceId,
1745
- signalName,
1746
- signalTag,
1747
- emittedAt: formatTimestamp(emittedAt),
1748
- consumed: false,
1749
- consumedBy: null,
1750
- isMeta
1787
+ ...existingSignalEmission,
1788
+ fullSignalName: existingSignalEmission.fullSignalName ?? signal,
1789
+ uuid: existingSignalEmission.uuid ?? uuid(),
1790
+ executionTraceId: existingSignalEmission.executionTraceId ?? executionTraceId,
1791
+ signalName: existingSignalEmission.signalName ?? signalName,
1792
+ signalTag: existingSignalEmission.signalTag ?? signalTag,
1793
+ emittedAt: existingSignalEmission.emittedAt ?? formatTimestamp(emittedAt),
1794
+ consumed: existingSignalEmission.consumed ?? false,
1795
+ consumedBy: existingSignalEmission.consumedBy ?? null,
1796
+ isMeta: existingSignalEmission.isMeta ?? isMeta,
1797
+ __traceCreatedBySignalBroker: existingSignalEmission.__traceCreatedBySignalBroker ?? isNewTrace
1751
1798
  };
1752
- this.emit("sub_meta.signal_broker.emitting_signal", { ...context });
1799
+ if (!isReceivedSignalTransmission) {
1800
+ if (isNewTrace) {
1801
+ context.__traceCreatedBySignalBroker = true;
1802
+ }
1803
+ this.emit("sub_meta.signal_broker.emitting_signal", { ...context });
1804
+ if (isNewTrace) {
1805
+ delete context.__traceCreatedBySignalBroker;
1806
+ }
1807
+ }
1753
1808
  } else if (isSubMeta) {
1754
1809
  context.__isSubMeta = true;
1755
1810
  }
1811
+ delete context.__receivedSignalTransmission;
1756
1812
  context.__metadata = {
1757
1813
  ...context.__metadata,
1758
1814
  __executionTraceId: executionTraceId
@@ -1816,8 +1872,11 @@ var SignalBroker = class _SignalBroker {
1816
1872
  * @param {string} signal - The name of the signal to be added.
1817
1873
  * @return {void} This method does not return any value.
1818
1874
  */
1819
- addSignal(signal) {
1875
+ addSignal(signal, metadata) {
1820
1876
  let _signal = signal;
1877
+ if (metadata) {
1878
+ this.setSignalMetadata(signal, metadata);
1879
+ }
1821
1880
  if (!this.signalObservers.has(_signal)) {
1822
1881
  this.validateSignalName(_signal);
1823
1882
  this.signalObservers.set(_signal, {
@@ -1847,11 +1906,14 @@ var SignalBroker = class _SignalBroker {
1847
1906
  * @param routineOrTask The observer.
1848
1907
  * @edge Duplicates ignored; supports wildcards for broad listening.
1849
1908
  */
1850
- observe(signal, routineOrTask) {
1851
- this.addSignal(signal);
1909
+ observe(signal, routineOrTask, metadata) {
1910
+ this.addSignal(signal, metadata);
1852
1911
  this.signalObservers.get(signal).tasks.add(routineOrTask);
1853
1912
  }
1854
- registerEmittedSignal(signal) {
1913
+ registerEmittedSignal(signal, metadata) {
1914
+ if (metadata) {
1915
+ this.setSignalMetadata(signal, metadata);
1916
+ }
1855
1917
  this.emittedSignalsRegistry.add(signal);
1856
1918
  }
1857
1919
  /**
@@ -1886,6 +1948,7 @@ var SignalBroker = class _SignalBroker {
1886
1948
  this.clearThrottleState();
1887
1949
  this.signalObservers.clear();
1888
1950
  this.emittedSignalsRegistry.clear();
1951
+ this.signalMetadataRegistry.clear();
1889
1952
  }
1890
1953
  shutdown() {
1891
1954
  this.reset();
@@ -2294,6 +2357,27 @@ var SignalEmitter = class {
2294
2357
  };
2295
2358
 
2296
2359
  // src/graph/execution/GraphNode.ts
2360
+ function normalizeGraphErrorMessage(error) {
2361
+ if (typeof error === "string") {
2362
+ return error;
2363
+ }
2364
+ if (error instanceof Error) {
2365
+ return error.message;
2366
+ }
2367
+ if (error && typeof error === "object") {
2368
+ const record = error;
2369
+ if (typeof record.__error === "string") {
2370
+ return record.__error;
2371
+ }
2372
+ if (typeof record.error === "string") {
2373
+ return record.error;
2374
+ }
2375
+ if (typeof record.message === "string") {
2376
+ return record.message;
2377
+ }
2378
+ }
2379
+ return String(error);
2380
+ }
2297
2381
  var GraphNode = class _GraphNode extends SignalEmitter {
2298
2382
  constructor(task, context, routineExecId, prevNodes = [], debug = false, verbose = false) {
2299
2383
  super(
@@ -2455,6 +2539,7 @@ var GraphNode = class _GraphNode extends SignalEmitter {
2455
2539
  uuid: this.id,
2456
2540
  routineExecutionId: this.routineExecId,
2457
2541
  executionTraceId: this.executionTraceId,
2542
+ inquiryId: context.__inquiryId ?? null,
2458
2543
  context: this.context.getContext(),
2459
2544
  metaContext: this.context.getMetadata(),
2460
2545
  taskName: this.task.name,
@@ -2477,7 +2562,8 @@ var GraphNode = class _GraphNode extends SignalEmitter {
2477
2562
  {
2478
2563
  data: {
2479
2564
  taskExecutionId: this.id,
2480
- previousTaskExecutionId: node.id
2565
+ previousTaskExecutionId: node.id,
2566
+ executionTraceId: context.__metadata?.__executionTraceId ?? context.__executionTraceId ?? null
2481
2567
  },
2482
2568
  filter: {
2483
2569
  taskName: this.task.name,
@@ -2495,7 +2581,8 @@ var GraphNode = class _GraphNode extends SignalEmitter {
2495
2581
  {
2496
2582
  data: {
2497
2583
  taskExecutionId: this.id,
2498
- previousTaskExecutionId: context.__previousTaskExecutionId
2584
+ previousTaskExecutionId: context.__previousTaskExecutionId,
2585
+ executionTraceId: context.__metadata?.__executionTraceId ?? context.__executionTraceId ?? null
2499
2586
  },
2500
2587
  filter: {
2501
2588
  taskName: this.task.name,
@@ -2732,7 +2819,14 @@ var GraphNode = class _GraphNode extends SignalEmitter {
2732
2819
  inquire(inquiry, context, options) {
2733
2820
  return Cadenza.resolveRuntimeInquiryDelegate()(
2734
2821
  inquiry,
2735
- { ...context, __executionTraceId: this.executionTraceId },
2822
+ {
2823
+ ...context,
2824
+ __executionTraceId: context.__executionTraceId ?? context.__metadata?.__executionTraceId ?? this.executionTraceId,
2825
+ __inquirySourceTaskName: this.task.name,
2826
+ __inquirySourceTaskVersion: this.task.version,
2827
+ __inquirySourceTaskExecutionId: this.id,
2828
+ __inquirySourceRoutineExecutionId: this.routineExecId
2829
+ },
2736
2830
  options
2737
2831
  );
2738
2832
  }
@@ -2902,11 +2996,12 @@ var GraphNode = class _GraphNode extends SignalEmitter {
2902
2996
  * @return {void} This method does not return any value.
2903
2997
  */
2904
2998
  onError(error, errorData = {}) {
2999
+ const normalizedError = normalizeGraphErrorMessage(error);
2905
3000
  this.result = {
2906
3001
  ...this.context.getFullContext(),
2907
- __error: `Node error: ${error}`,
3002
+ __error: `Node error: ${normalizedError}`,
2908
3003
  __retries: this.retries,
2909
- error: `Node error: ${error}`,
3004
+ error: `Node error: ${normalizedError}`,
2910
3005
  errored: true,
2911
3006
  returnedValue: this.result,
2912
3007
  ...errorData
@@ -3481,8 +3576,16 @@ var GraphRunner = class extends SignalEmitter {
3481
3576
  const isNewTrace = !context.__routineExecId && !context.__metadata?.__executionTraceId && !context.__executionTraceId;
3482
3577
  const executionTraceId = context.__metadata?.__executionTraceId ?? context.__executionTraceId ?? uuid5();
3483
3578
  context.__executionTraceId = executionTraceId;
3579
+ context.__traceCreatedByRunner = isNewTrace;
3580
+ const isNewRoutine = !context.__routineExecId;
3484
3581
  const routineExecId = context.__routineExecId ?? uuid5();
3485
3582
  context.__routineExecId = routineExecId;
3583
+ const routineCreatedAt = formatTimestamp(Date.now());
3584
+ context.__routineCreatedByRunner = isNewRoutine;
3585
+ context.__routineName = routineName;
3586
+ context.__routineVersion = routineVersion;
3587
+ context.__routineCreatedAt = routineCreatedAt;
3588
+ context.__routineIsMeta = isMeta;
3486
3589
  Cadenza.applyRuntimeValidationScopesToContext(
3487
3590
  context,
3488
3591
  routineName,
@@ -3517,8 +3620,7 @@ var GraphRunner = class extends SignalEmitter {
3517
3620
  executionTraceId,
3518
3621
  context: ctx.getContext(),
3519
3622
  metaContext: ctx.getMetadata(),
3520
- previousRoutineExecution: context.__localRoutineExecId ?? context.__metadata?.__routineExecId ?? null,
3521
- created: formatTimestamp(Date.now())
3623
+ created: routineCreatedAt
3522
3624
  },
3523
3625
  __metadata: {
3524
3626
  __executionTraceId: executionTraceId
@@ -3751,6 +3853,7 @@ var Actor = class {
3751
3853
  this.stateByKey = /* @__PURE__ */ new Map();
3752
3854
  this.sessionByKey = /* @__PURE__ */ new Map();
3753
3855
  this.idempotencyByKey = /* @__PURE__ */ new Map();
3856
+ this.writeQueueByKey = /* @__PURE__ */ new Map();
3754
3857
  this.nextTaskBindingIndex = 0;
3755
3858
  if (!spec.name || typeof spec.name !== "string") {
3756
3859
  throw new Error("Actor name must be a non-empty string");
@@ -3932,7 +4035,7 @@ var Actor = class {
3932
4035
  taskBindingId,
3933
4036
  actorKey,
3934
4037
  invocationOptions,
3935
- runTask
4038
+ () => this.runWithPerKeyWriteSerialization(actorKey, mode, runTask)
3936
4039
  );
3937
4040
  };
3938
4041
  wrapped[ACTOR_TASK_METADATA] = {
@@ -4210,6 +4313,24 @@ var Actor = class {
4210
4313
  });
4211
4314
  return promise;
4212
4315
  }
4316
+ runWithPerKeyWriteSerialization(actorKey, mode, runTask) {
4317
+ if (mode === "read") {
4318
+ return runTask();
4319
+ }
4320
+ const previous = this.writeQueueByKey.get(actorKey) ?? Promise.resolve();
4321
+ let releaseCurrent;
4322
+ const currentGate = new Promise((resolve) => {
4323
+ releaseCurrent = resolve;
4324
+ });
4325
+ const currentTail = previous.catch(() => void 0).then(() => currentGate);
4326
+ this.writeQueueByKey.set(actorKey, currentTail);
4327
+ return previous.catch(() => void 0).then(runTask).finally(() => {
4328
+ releaseCurrent();
4329
+ if (this.writeQueueByKey.get(actorKey) === currentTail) {
4330
+ this.writeQueueByKey.delete(actorKey);
4331
+ }
4332
+ });
4333
+ }
4213
4334
  getActiveIdempotencyRecord(compositeKey) {
4214
4335
  const record = this.idempotencyByKey.get(compositeKey);
4215
4336
  if (!record) {
@@ -4227,23 +4348,25 @@ var Actor = class {
4227
4348
  return;
4228
4349
  }
4229
4350
  const timeoutMs = normalizePositiveInteger(this.spec.session?.persistenceTimeoutMs) ?? 5e3;
4351
+ const persistenceContext = {
4352
+ actor_name: this.spec.name,
4353
+ actor_version: 1,
4354
+ actor_key: actorKey,
4355
+ durable_state: cloneForDurableState(durableState),
4356
+ durable_version: durableVersion,
4357
+ expires_at: null
4358
+ };
4230
4359
  const response = await inquire(
4231
4360
  META_ACTOR_SESSION_STATE_PERSIST_INTENT,
4232
- {
4233
- actor_name: this.spec.name,
4234
- actor_version: 1,
4235
- actor_key: actorKey,
4236
- durable_state: cloneForDurableState(durableState),
4237
- durable_version: durableVersion,
4238
- expires_at: null
4239
- },
4361
+ persistenceContext,
4240
4362
  {
4241
4363
  timeout: timeoutMs,
4364
+ requireComplete: true,
4242
4365
  rejectOnTimeout: true
4243
4366
  }
4244
4367
  );
4245
4368
  if (!isObject2(response) || response.__success !== true || response.persisted !== true) {
4246
- const reason = isObject2(response) ? response.__error ?? response.error : void 0;
4369
+ const reason = isObject2(response) ? response.__error ?? response.error ?? response.internalError ?? (response.errored === true ? `errored response keys: ${Object.keys(response).join(",")}` : response.failed === true ? `failed response keys: ${Object.keys(response).join(",")}` : void 0) : void 0;
4247
4370
  throw new Error(
4248
4371
  `Actor "${this.spec.name}" durable state persistence failed for key "${actorKey}"${reason ? `: ${String(reason)}` : ""}`
4249
4372
  );
@@ -4277,6 +4400,20 @@ var Actor = class {
4277
4400
  };
4278
4401
 
4279
4402
  // src/graph/definition/Task.ts
4403
+ function normalizeSignalDefinition(input) {
4404
+ if (typeof input === "string") {
4405
+ return {
4406
+ name: input
4407
+ };
4408
+ }
4409
+ return {
4410
+ name: input.name,
4411
+ metadata: {
4412
+ deliveryMode: input.deliveryMode,
4413
+ broadcastFilter: input.broadcastFilter ?? null
4414
+ }
4415
+ };
4416
+ }
4280
4417
  var Task = class _Task extends SignalEmitter {
4281
4418
  /**
4282
4419
  * Constructs an instance of the task with the specified properties and configuration options.
@@ -4372,6 +4509,7 @@ var Task = class _Task extends SignalEmitter {
4372
4509
  "meta.task.output_schema_missing",
4373
4510
  "meta.task.relationship_added",
4374
4511
  "meta.task.relationship_removed",
4512
+ "meta.task.intent_associated",
4375
4513
  "meta.task.layer_index_changed",
4376
4514
  "meta.node.scheduled",
4377
4515
  "meta.node.mapped",
@@ -5096,9 +5234,10 @@ var Task = class _Task extends SignalEmitter {
5096
5234
  * @return {this} The current instance after adding the specified signals.
5097
5235
  */
5098
5236
  doOn(...signals) {
5099
- signals.forEach((signal) => {
5237
+ signals.forEach((input) => {
5238
+ const { name: signal, metadata } = normalizeSignalDefinition(input);
5100
5239
  if (this.observedSignals.has(signal)) return;
5101
- Cadenza.signalBroker.observe(signal, this);
5240
+ Cadenza.signalBroker.observe(signal, this, metadata);
5102
5241
  this.observedSignals.add(signal);
5103
5242
  if (this.register) {
5104
5243
  this.emitWithMetadata("meta.task.observed_signal", {
@@ -5119,13 +5258,14 @@ var Task = class _Task extends SignalEmitter {
5119
5258
  * @return {this} The current instance for method chaining.
5120
5259
  */
5121
5260
  emits(...signals) {
5122
- signals.forEach((signal) => {
5261
+ signals.forEach((input) => {
5262
+ const { name: signal } = normalizeSignalDefinition(input);
5123
5263
  if (this.observedSignals.has(signal))
5124
5264
  throw new Error(
5125
5265
  `Detected signal loop for task ${this.name}. Signal name: ${signal}`
5126
5266
  );
5127
5267
  this.signalsToEmitAfter.add(signal);
5128
- this.attachSignal(signal);
5268
+ this.attachSignal(input);
5129
5269
  });
5130
5270
  return this;
5131
5271
  }
@@ -5137,9 +5277,10 @@ var Task = class _Task extends SignalEmitter {
5137
5277
  * @return {this} Returns the current instance for chaining.
5138
5278
  */
5139
5279
  emitsOnFail(...signals) {
5140
- signals.forEach((signal) => {
5280
+ signals.forEach((input) => {
5281
+ const { name: signal } = normalizeSignalDefinition(input);
5141
5282
  this.signalsToEmitOnFail.add(signal);
5142
- this.attachSignal(signal);
5283
+ this.attachSignal(input);
5143
5284
  });
5144
5285
  return this;
5145
5286
  }
@@ -5150,9 +5291,10 @@ var Task = class _Task extends SignalEmitter {
5150
5291
  * @return {void} This method does not return a value.
5151
5292
  */
5152
5293
  attachSignal(...signals) {
5153
- signals.forEach((signal) => {
5294
+ signals.forEach((input) => {
5295
+ const { name: signal, metadata } = normalizeSignalDefinition(input);
5154
5296
  this.emitsSignals.add(signal);
5155
- Cadenza.signalBroker.registerEmittedSignal(signal);
5297
+ Cadenza.signalBroker.registerEmittedSignal(signal, metadata);
5156
5298
  if (this.register) {
5157
5299
  const data = {
5158
5300
  signals: {
@@ -5246,8 +5388,20 @@ var Task = class _Task extends SignalEmitter {
5246
5388
  }
5247
5389
  respondsTo(...inquires) {
5248
5390
  for (const intentName of inquires) {
5391
+ if (this.handlesIntents.has(intentName)) {
5392
+ continue;
5393
+ }
5249
5394
  this.handlesIntents.add(intentName);
5250
5395
  Cadenza.inquiryBroker.observe(intentName, this);
5396
+ if (this.register) {
5397
+ this.emitWithMetadata("meta.task.intent_associated", {
5398
+ data: {
5399
+ intentName,
5400
+ taskName: this.name,
5401
+ taskVersion: this.version
5402
+ }
5403
+ });
5404
+ }
5251
5405
  const intent = Cadenza.inquiryBroker.intents.get(intentName);
5252
5406
  if (intent?.input) {
5253
5407
  this.inputContextSchema = this.mergeSchemaVariant(