@hotmeshio/hotmesh 0.14.7 → 0.14.8

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.14.7",
3
+ "version": "0.14.8",
4
4
  "description": "Durable Workflow",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -181,6 +181,20 @@ class Worker extends activity_1.Activity {
181
181
  retry: this.config.retry,
182
182
  };
183
183
  }
184
+ // Propagate per-activity retry config as _streamRetryConfig so
185
+ // the engine-level retry mechanism uses it for exponential backoff.
186
+ // The durable module's maximumAttempts means max retries
187
+ // (total executions = 1 + maximumAttempts), while the engine's
188
+ // max_retry_attempts means total attempts. Add 1 to align.
189
+ if (jobData?.maximumAttempts || jobData?.backoffCoefficient || jobData?.maximumInterval || jobData?.initialInterval) {
190
+ const durableMaxAttempts = jobData.maximumAttempts ?? 50;
191
+ streamData._streamRetryConfig = {
192
+ max_retry_attempts: durableMaxAttempts + 1,
193
+ backoff_coefficient: jobData.backoffCoefficient ?? 10,
194
+ maximum_interval_seconds: jobData.maximumInterval ?? 120,
195
+ initialInterval: jobData.initialInterval ?? 1,
196
+ };
197
+ }
184
198
  return (await this.engine.router?.publishMessage(topic, streamData, transaction));
185
199
  }
186
200
  }
@@ -474,10 +474,10 @@ class ExporterService {
474
474
  const activityArgsField = this.resolveSymbolField(symbolSets, 'activity_trigger', 'activity_trigger/output/data/arguments');
475
475
  const workflowArgsField = this.resolveSymbolField(symbolSets, 'trigger', 'trigger/output/data/arguments');
476
476
  // ── 1. Enrich activity inputs ──
477
- if (activityArgsField) {
477
+ {
478
478
  const activityEvents = execution.events.filter((e) => e.event_type === 'activity_task_scheduled' || e.event_type === 'activity_task_completed' || e.event_type === 'activity_task_failed');
479
479
  if (activityEvents.length > 0) {
480
- const { byJobId, byNameIndex } = await this.store.getActivityInputs(workflowId, activityArgsField);
480
+ const { byJobId, byNameIndex } = await this.store.getActivityInputs(workflowId, activityArgsField || '');
481
481
  for (const evt of activityEvents) {
482
482
  const attrs = evt.attributes;
483
483
  let input = attrs.timeline_key ? byJobId.get(attrs.timeline_key) : undefined;
@@ -1,22 +1,4 @@
1
- /**
2
- *********** HOTMESH 'DURABLE' MODULE APPLICATION GRAPH **********
3
- *
4
- * This HotMesh application spec uses 50 activities and 25 transitions
5
- * to model a durable workflow engine using a pluggable backend.
6
- *
7
- * This YAML file can also serve as a useful starting point for building
8
- * Integration/BPM/Workflow servers in general (MuleSoft, etc) without the need
9
- * for a physical application server.
10
- *
11
- * Possible use cases include:
12
- * * Orchestration servers
13
- * * Integration servers
14
- * * BPMN engines
15
- * * Reentrant process servers
16
- * * Service Meshes
17
- * * Master Data Management systems
18
- */
19
- declare const APP_VERSION = "11";
1
+ declare const APP_VERSION = "12";
20
2
  declare const APP_ID = "durable";
21
3
  /**
22
4
  * returns a new durable workflow schema
@@ -1,25 +1,7 @@
1
1
  "use strict";
2
- /**
3
- *********** HOTMESH 'DURABLE' MODULE APPLICATION GRAPH **********
4
- *
5
- * This HotMesh application spec uses 50 activities and 25 transitions
6
- * to model a durable workflow engine using a pluggable backend.
7
- *
8
- * This YAML file can also serve as a useful starting point for building
9
- * Integration/BPM/Workflow servers in general (MuleSoft, etc) without the need
10
- * for a physical application server.
11
- *
12
- * Possible use cases include:
13
- * * Orchestration servers
14
- * * Integration servers
15
- * * BPMN engines
16
- * * Reentrant process servers
17
- * * Service Meshes
18
- * * Master Data Management systems
19
- */
20
2
  Object.defineProperty(exports, "__esModule", { value: true });
21
3
  exports.APP_ID = exports.APP_VERSION = exports.getWorkflowYAML = void 0;
22
- const APP_VERSION = '11';
4
+ const APP_VERSION = '12';
23
5
  exports.APP_VERSION = APP_VERSION;
24
6
  const APP_ID = 'durable';
25
7
  exports.APP_ID = APP_ID;
@@ -153,12 +135,10 @@ const getWorkflowYAML = (app, version) => {
153
135
  type: number
154
136
  maps:
155
137
  retryCount: 0
156
- throttleSeconds: 0
157
138
 
158
139
  throttler:
159
- title: Pauses for an exponentially-throttled amount of time after a retryable error or passes through immediately
140
+ title: Pass-through hook between cycle_hook and worker
160
141
  type: hook
161
- sleep: '{cycle_hook.output.data.throttleSeconds}'
162
142
 
163
143
  worker:
164
144
  title: Main Worker - Calls linked Workflow functions
@@ -202,6 +182,22 @@ const getWorkflowYAML = (app, version) => {
202
182
  - ['{trigger.output.data.maximumAttempts}', 5]
203
183
  - ['{@conditional.nullish}']
204
184
  - ['{@conditional.less_than}']
185
+ maximumAttempts:
186
+ '@pipe':
187
+ - ['{trigger.output.data.maximumAttempts}', 5]
188
+ - ['{@conditional.nullish}']
189
+ backoffCoefficient:
190
+ '@pipe':
191
+ - ['{trigger.output.data.backoffCoefficient}', 10]
192
+ - ['{@conditional.nullish}']
193
+ maximumInterval:
194
+ '@pipe':
195
+ - ['{trigger.output.data.maximumInterval}', 120]
196
+ - ['{@conditional.nullish}']
197
+ initialInterval:
198
+ '@pipe':
199
+ - ['{trigger.output.data.initialInterval}', 1]
200
+ - ['{@conditional.nullish}']
205
201
  output:
206
202
  schema:
207
203
  type: object
@@ -364,7 +360,6 @@ const getWorkflowYAML = (app, version) => {
364
360
  input:
365
361
  maps:
366
362
  retryCount: 0
367
- throttleSeconds: 0
368
363
  continueGeneration: '{cycle_hook.output.data.continueGeneration}'
369
364
  continueArgs: '{cycle_hook.output.data.continueArgs}'
370
365
 
@@ -540,38 +535,13 @@ const getWorkflowYAML = (app, version) => {
540
535
  - '@pipe':
541
536
  - [0]
542
537
  - ['{@conditional.ternary}']
543
- throttleSeconds:
544
- '@pipe':
545
- - '@pipe':
546
- - ['{childer.output.metadata.err}']
547
- - '@pipe':
548
- - '@pipe':
549
- - '@pipe':
550
- - '@pipe':
551
- - ['{trigger.output.data.backoffCoefficient}', 10]
552
- - ['{@conditional.nullish}']
553
- - '@pipe':
554
- - ['{cycle_hook.output.data.retryCount}', 0]
555
- - ['{@conditional.nullish}']
556
- - ['{@math.pow}']
557
- - '@pipe':
558
- - ['{trigger.output.data.initialInterval}', 1]
559
- - ['{@conditional.nullish}']
560
- - ['{@math.multiply}']
561
- - '@pipe':
562
- - ['{trigger.output.data.maximumInterval}', 120]
563
- - ['{@logical.or}']
564
- - ['{@math.min}']
565
- - '@pipe':
566
- - [0]
567
- - ['{@conditional.ternary}']
568
538
  continueGeneration: '{cycle_hook.output.data.continueGeneration}'
569
539
  continueArgs: '{cycle_hook.output.data.continueArgs}'
570
540
 
571
541
  proxyer:
572
- title: Invokes the activity flow and awaits the response
573
- type: await
574
- topic: ${app}.activity.execute
542
+ title: Calls the activity function directly via worker topic
543
+ type: worker
544
+ topic: '{worker.output.data.workflowTopic}'
575
545
  input:
576
546
  schema:
577
547
  type: object
@@ -637,6 +607,12 @@ const getWorkflowYAML = (app, version) => {
637
607
  - ['{worker.output.data.maximumInterval}','{trigger.output.data.maximumInterval}']
638
608
  - ['{@conditional.nullish}', 120]
639
609
  - ['{@conditional.nullish}']
610
+ output:
611
+ schema:
612
+ type: object
613
+ properties:
614
+ response:
615
+ type: any
640
616
  job:
641
617
  maps:
642
618
  idempotentcy-marker[-]:
@@ -646,24 +622,26 @@ const getWorkflowYAML = (app, version) => {
646
622
  - ['{@string.concat}']
647
623
  - '@pipe':
648
624
  - '@pipe':
649
- - ['{$self.output.data.$error}']
625
+ - ['{$self.output.data.$error}', '{$self.output.metadata.err}']
626
+ - ['{@logical.or}']
650
627
  - '@pipe':
651
628
  - '@pipe':
652
629
  - [$error]
653
630
  - '@pipe':
654
- - ['{$self.output.data.$error}']
631
+ - ['{$self.output.data.$error}', '{$self.output.metadata.$error}']
632
+ - ['{@conditional.nullish}']
655
633
  - '@pipe':
656
634
  - [ac]
657
635
  - '@pipe':
658
- - ['{$self.output.data.jc}']
636
+ - ['{$self.output.metadata.ac}']
659
637
  - '@pipe':
660
638
  - [au]
661
639
  - '@pipe':
662
- - ['{$self.output.data.ju}']
640
+ - ['{$self.output.metadata.au}']
663
641
  - '@pipe':
664
642
  - ['job_id']
665
643
  - '@pipe':
666
- - ['{$self.output.data.workflowId}']
644
+ - ['{worker.output.data.workflowId}']
667
645
  - ['{@object.create}']
668
646
  - '@pipe':
669
647
  - '@pipe':
@@ -673,15 +651,15 @@ const getWorkflowYAML = (app, version) => {
673
651
  - '@pipe':
674
652
  - [ac]
675
653
  - '@pipe':
676
- - ['{$self.output.data.jc}']
654
+ - ['{$self.output.metadata.ac}']
677
655
  - '@pipe':
678
656
  - [au]
679
657
  - '@pipe':
680
- - ['{$self.output.data.ju}']
658
+ - ['{$self.output.metadata.au}']
681
659
  - '@pipe':
682
660
  - ['job_id']
683
661
  - '@pipe':
684
- - ['{$self.output.data.workflowId}']
662
+ - ['{worker.output.data.workflowId}']
685
663
  - ['{@object.create}']
686
664
  - ['{@conditional.ternary}']
687
665
  - ['{@object.create}']
@@ -692,42 +670,7 @@ const getWorkflowYAML = (app, version) => {
692
670
  ancestor: cycle_hook
693
671
  input:
694
672
  maps:
695
- retryCount:
696
- '@pipe':
697
- - '@pipe':
698
- - ['{proxyer.output.metadata.err}']
699
- - '@pipe':
700
- - ['{cycle_hook.output.data.retryCount}', 0]
701
- - ['{@logical.or}', 1]
702
- - ['{@math.add}']
703
- - '@pipe':
704
- - [0]
705
- - ['{@conditional.ternary}']
706
- throttleSeconds:
707
- '@pipe':
708
- - '@pipe':
709
- - ['{proxyer.output.metadata.err}']
710
- - '@pipe':
711
- - '@pipe':
712
- - '@pipe':
713
- - '@pipe':
714
- - ['{trigger.output.data.backoffCoefficient}', 10]
715
- - ['{@conditional.nullish}']
716
- - '@pipe':
717
- - ['{cycle_hook.output.data.retryCount}', 0]
718
- - ['{@conditional.nullish}']
719
- - ['{@math.pow}']
720
- - '@pipe':
721
- - ['{trigger.output.data.initialInterval}', 1]
722
- - ['{@conditional.nullish}']
723
- - ['{@math.multiply}']
724
- - '@pipe':
725
- - ['{trigger.output.data.maximumInterval}', 120]
726
- - ['{@logical.or}']
727
- - ['{@math.min}']
728
- - '@pipe':
729
- - [0]
730
- - ['{@conditional.ternary}']
673
+ retryCount: 0
731
674
  continueGeneration: '{cycle_hook.output.data.continueGeneration}'
732
675
  continueArgs: '{cycle_hook.output.data.continueArgs}'
733
676
 
@@ -832,35 +775,6 @@ const getWorkflowYAML = (app, version) => {
832
775
  input:
833
776
  maps:
834
777
  retryCount: 0
835
- throttleSeconds: 0
836
- continueGeneration: '{cycle_hook.output.data.continueGeneration}'
837
- continueArgs: '{cycle_hook.output.data.continueArgs}'
838
-
839
- retryer:
840
- title: Cycles back to the cycle_hook pivot, increasing the retryCount (the exponential)
841
- type: cycle
842
- ancestor: cycle_hook
843
- input:
844
- maps:
845
- retryCount:
846
- '@pipe':
847
- - ['{cycle_hook.output.data.retryCount}', 0]
848
- - ['{@logical.or}', 1]
849
- - ['{@math.add}']
850
- throttleSeconds:
851
- '@pipe':
852
- - '@pipe':
853
- - '@pipe':
854
- - ['{trigger.output.data.backoffCoefficient}', 10]
855
- - ['{@conditional.nullish}']
856
- - '@pipe':
857
- - ['{cycle_hook.output.data.retryCount}', 0]
858
- - ['{@conditional.nullish}']
859
- - ['{@math.pow}']
860
- - '@pipe':
861
- - ['{trigger.output.data.maximumInterval}', 120]
862
- - ['{@logical.or}']
863
- - ['{@math.min}']
864
778
  continueGeneration: '{cycle_hook.output.data.continueGeneration}'
865
779
  continueArgs: '{cycle_hook.output.data.continueArgs}'
866
780
 
@@ -871,7 +785,6 @@ const getWorkflowYAML = (app, version) => {
871
785
  input:
872
786
  maps:
873
787
  retryCount: 0
874
- throttleSeconds: 0
875
788
  continueArgs: '{worker.output.data.arguments}'
876
789
  continueGeneration:
877
790
  '@pipe':
@@ -886,18 +799,20 @@ const getWorkflowYAML = (app, version) => {
886
799
  job:
887
800
  maps:
888
801
  done: true
889
- $error: '{worker.output.data.$error}'
802
+ $error:
803
+ '@pipe':
804
+ - ['{worker.output.data.$error}', '{worker.output.metadata.$error}']
805
+ - ['{@conditional.nullish}']
890
806
  jc: '{$job.metadata.jc}'
891
807
  ju:
892
808
  '@pipe':
893
809
  - ['{@date.toISOXString}']
894
810
 
895
- closer:
896
- title: Closes the \`Signal In\` Hook Channel, so the workflow can exit
811
+ stopper:
812
+ title: Stops 'Signal In' when retryCount is exceeded (and as a result ends the job)
897
813
  type: signal
898
814
  subtype: one
899
815
  topic: ${app}.flow.signal
900
- statusThreshold: 1
901
816
  signal:
902
817
  schema:
903
818
  type: object
@@ -909,17 +824,26 @@ const getWorkflowYAML = (app, version) => {
909
824
  job:
910
825
  maps:
911
826
  done: true
912
- $error: '{worker.output.data.$error}'
827
+ $error:
828
+ '@pipe':
829
+ - '@pipe':
830
+ - ['{worker.output.data.$error}', '{worker.output.metadata.$error}']
831
+ - ['{@conditional.nullish}']
832
+ - '@pipe':
833
+ - [message, 'maximum retry attempts exceeded', code, 597]
834
+ - ['{@object.create}']
835
+ - ['{@conditional.nullish}']
913
836
  jc: '{$job.metadata.jc}'
914
837
  ju:
915
838
  '@pipe':
916
839
  - ['{@date.toISOXString}']
917
840
 
918
- stopper:
919
- title: Stops 'Signal In' when retryCount is exceeded (and as a result ends the job)
841
+ closer:
842
+ title: Closes the \`Signal In\` Hook Channel, so the workflow can exit
920
843
  type: signal
921
844
  subtype: one
922
845
  topic: ${app}.flow.signal
846
+ statusThreshold: 1
923
847
  signal:
924
848
  schema:
925
849
  type: object
@@ -933,8 +857,8 @@ const getWorkflowYAML = (app, version) => {
933
857
  done: true
934
858
  $error:
935
859
  '@pipe':
936
- - ['{worker.output.data.$error}', 'code', 597]
937
- - ['{@object.set}']
860
+ - ['{worker.output.data.$error}', '{worker.output.metadata.$error}']
861
+ - ['{@conditional.nullish}']
938
862
  jc: '{$job.metadata.jc}'
939
863
  ju:
940
864
  '@pipe':
@@ -970,13 +894,11 @@ const getWorkflowYAML = (app, version) => {
970
894
  type: number
971
895
  maps:
972
896
  retryCount: 0
973
- throttleSeconds: 0
974
897
 
975
898
  signaler_throttler:
976
- title: Pauses between failed hook executions for an exponentially-throttled amount of time after a retryable error
899
+ title: Pass-through hook between signaler_cycle_hook and signaler_worker
977
900
  type: hook
978
- sleep: '{signaler_cycle_hook.output.data.throttleSeconds}'
979
-
901
+
980
902
  signaler_worker:
981
903
  title: Signal In - Worker
982
904
  type: worker
@@ -1014,6 +936,22 @@ const getWorkflowYAML = (app, version) => {
1014
936
  - ['{trigger.output.data.maximumAttempts}', 5]
1015
937
  - ['{@conditional.nullish}']
1016
938
  - ['{@conditional.less_than}']
939
+ maximumAttempts:
940
+ '@pipe':
941
+ - ['{trigger.output.data.maximumAttempts}', 5]
942
+ - ['{@conditional.nullish}']
943
+ backoffCoefficient:
944
+ '@pipe':
945
+ - ['{trigger.output.data.backoffCoefficient}', 10]
946
+ - ['{@conditional.nullish}']
947
+ maximumInterval:
948
+ '@pipe':
949
+ - ['{trigger.output.data.maximumInterval}', 120]
950
+ - ['{@conditional.nullish}']
951
+ initialInterval:
952
+ '@pipe':
953
+ - ['{trigger.output.data.initialInterval}', 1]
954
+ - ['{@conditional.nullish}']
1017
955
 
1018
956
  output:
1019
957
  schema:
@@ -1174,7 +1112,6 @@ const getWorkflowYAML = (app, version) => {
1174
1112
  input:
1175
1113
  maps:
1176
1114
  retryCount: 0
1177
- throttleSeconds: 0
1178
1115
 
1179
1116
  signaler_childer:
1180
1117
  title: Awaits a child flow to be executed/started
@@ -1344,36 +1281,11 @@ const getWorkflowYAML = (app, version) => {
1344
1281
  - '@pipe':
1345
1282
  - [0]
1346
1283
  - ['{@conditional.ternary}']
1347
- throttleSeconds:
1348
- '@pipe':
1349
- - '@pipe':
1350
- - ['{signaler_childer.output.metadata.err}']
1351
- - '@pipe':
1352
- - '@pipe':
1353
- - '@pipe':
1354
- - '@pipe':
1355
- - ['{trigger.output.data.backoffCoefficient}', 10]
1356
- - ['{@conditional.nullish}']
1357
- - '@pipe':
1358
- - ['{signaler_cycle_hook.output.data.retryCount}', 0]
1359
- - ['{@conditional.nullish}']
1360
- - ['{@math.pow}']
1361
- - '@pipe':
1362
- - ['{trigger.output.data.initialInterval}', 1]
1363
- - ['{@conditional.nullish}']
1364
- - ['{@math.multiply}']
1365
- - '@pipe':
1366
- - ['{trigger.output.data.maximumInterval}', 120]
1367
- - ['{@logical.or}']
1368
- - ['{@math.min}']
1369
- - '@pipe':
1370
- - [0]
1371
- - ['{@conditional.ternary}']
1372
-
1284
+
1373
1285
  signaler_proxyer:
1374
- title: Invokes the activity flow and awaits the response
1375
- type: await
1376
- topic: ${app}.activity.execute
1286
+ title: Calls the activity function directly via worker topic
1287
+ type: worker
1288
+ topic: '{signaler_worker.output.data.workflowTopic}'
1377
1289
  input:
1378
1290
  schema:
1379
1291
  type: object
@@ -1439,6 +1351,12 @@ const getWorkflowYAML = (app, version) => {
1439
1351
  - ['{signaler_worker.output.data.maximumInterval}','{trigger.output.data.maximumInterval}']
1440
1352
  - ['{@conditional.nullish}', 120]
1441
1353
  - ['{@conditional.nullish}']
1354
+ output:
1355
+ schema:
1356
+ type: object
1357
+ properties:
1358
+ response:
1359
+ type: any
1442
1360
  job:
1443
1361
  maps:
1444
1362
  idempotentcy-marker[-]:
@@ -1448,24 +1366,26 @@ const getWorkflowYAML = (app, version) => {
1448
1366
  - ['{@string.concat}']
1449
1367
  - '@pipe':
1450
1368
  - '@pipe':
1451
- - ['{$self.output.data.$error}']
1369
+ - ['{$self.output.data.$error}', '{$self.output.metadata.err}']
1370
+ - ['{@logical.or}']
1452
1371
  - '@pipe':
1453
1372
  - '@pipe':
1454
1373
  - [$error]
1455
1374
  - '@pipe':
1456
- - ['{$self.output.data.$error}']
1375
+ - ['{$self.output.data.$error}', '{$self.output.metadata.$error}']
1376
+ - ['{@conditional.nullish}']
1457
1377
  - '@pipe':
1458
1378
  - [ac]
1459
1379
  - '@pipe':
1460
- - ['{$self.output.data.jc}']
1380
+ - ['{$self.output.metadata.ac}']
1461
1381
  - '@pipe':
1462
1382
  - [au]
1463
1383
  - '@pipe':
1464
- - ['{$self.output.data.ju}']
1384
+ - ['{$self.output.metadata.au}']
1465
1385
  - '@pipe':
1466
1386
  - ['job_id']
1467
1387
  - '@pipe':
1468
- - ['{$self.output.data.workflowId}']
1388
+ - ['{signaler_worker.output.data.workflowId}']
1469
1389
  - ['{@object.create}']
1470
1390
  - '@pipe':
1471
1391
  - '@pipe':
@@ -1475,15 +1395,15 @@ const getWorkflowYAML = (app, version) => {
1475
1395
  - '@pipe':
1476
1396
  - [ac]
1477
1397
  - '@pipe':
1478
- - ['{$self.output.data.jc}']
1398
+ - ['{$self.output.metadata.ac}']
1479
1399
  - '@pipe':
1480
1400
  - [au]
1481
1401
  - '@pipe':
1482
- - ['{$self.output.data.ju}']
1402
+ - ['{$self.output.metadata.au}']
1483
1403
  - '@pipe':
1484
1404
  - ['job_id']
1485
1405
  - '@pipe':
1486
- - ['{$self.output.data.workflowId}']
1406
+ - ['{signaler_worker.output.data.workflowId}']
1487
1407
  - ['{@object.create}']
1488
1408
  - ['{@conditional.ternary}']
1489
1409
  - ['{@object.create}']
@@ -1494,42 +1414,7 @@ const getWorkflowYAML = (app, version) => {
1494
1414
  ancestor: signaler_cycle_hook
1495
1415
  input:
1496
1416
  maps:
1497
- retryCount:
1498
- '@pipe':
1499
- - '@pipe':
1500
- - ['{signaler_proxyer.output.metadata.err}']
1501
- - '@pipe':
1502
- - ['{signaler_cycle_hook.output.data.retryCount}', 0]
1503
- - ['{@logical.or}', 1]
1504
- - ['{@math.add}']
1505
- - '@pipe':
1506
- - [0]
1507
- - ['{@conditional.ternary}']
1508
- throttleSeconds:
1509
- '@pipe':
1510
- - '@pipe':
1511
- - ['{signaler_proxyer.output.metadata.err}']
1512
- - '@pipe':
1513
- - '@pipe':
1514
- - '@pipe':
1515
- - '@pipe':
1516
- - ['{trigger.output.data.backoffCoefficient}', 10]
1517
- - ['{@conditional.nullish}']
1518
- - '@pipe':
1519
- - ['{signaler_cycle_hook.output.data.retryCount}', 0]
1520
- - ['{@conditional.nullish}']
1521
- - ['{@math.pow}']
1522
- - '@pipe':
1523
- - ['{trigger.output.data.initialInterval}', 1]
1524
- - ['{@conditional.nullish}']
1525
- - ['{@math.multiply}']
1526
- - '@pipe':
1527
- - ['{trigger.output.data.maximumInterval}', 120]
1528
- - ['{@logical.or}']
1529
- - ['{@math.min}']
1530
- - '@pipe':
1531
- - [0]
1532
- - ['{@conditional.ternary}']
1417
+ retryCount: 0
1533
1418
 
1534
1419
  signaler_collator:
1535
1420
  title: Awaits the collator to resolve the idempotent items as a sequential set
@@ -1632,34 +1517,7 @@ const getWorkflowYAML = (app, version) => {
1632
1517
  input:
1633
1518
  maps:
1634
1519
  retryCount: 0
1635
- throttleSeconds: 0
1636
1520
 
1637
- signaler_retryer:
1638
- title: Cycles back to the signaler_cycle_hook pivot, increasing the retryCount (the exponential)
1639
- type: cycle
1640
- ancestor: signaler_cycle_hook
1641
- input:
1642
- maps:
1643
- retryCount:
1644
- '@pipe':
1645
- - ['{signaler_cycle_hook.output.data.retryCount}', 0]
1646
- - ['{@logical.or}', 1]
1647
- - ['{@math.add}']
1648
- throttleSeconds:
1649
- '@pipe':
1650
- - '@pipe':
1651
- - '@pipe':
1652
- - ['{trigger.output.data.backoffCoefficient}', 10]
1653
- - ['{@conditional.nullish}']
1654
- - '@pipe':
1655
- - ['{signaler_cycle_hook.output.data.retryCount}', 0]
1656
- - ['{@conditional.nullish}']
1657
- - ['{@math.pow}']
1658
- - '@pipe':
1659
- - ['{trigger.output.data.maximumInterval}', 120]
1660
- - ['{@logical.or}']
1661
- - ['{@math.min}']
1662
-
1663
1521
  transitions:
1664
1522
  trigger:
1665
1523
  - to: cycle_hook
@@ -1720,22 +1578,9 @@ const getWorkflowYAML = (app, version) => {
1720
1578
  - to: continuer
1721
1579
  conditions:
1722
1580
  code: 592
1723
- - to: retryer
1724
- conditions:
1725
- code: 599
1726
- match:
1727
- - expected: true
1728
- actual:
1729
- '@pipe':
1730
- - '@pipe':
1731
- - ['{cycle_hook.output.data.retryCount}']
1732
- - '@pipe':
1733
- - ['{trigger.output.data.maximumAttempts}', 5]
1734
- - ['{@conditional.nullish}']
1735
- - ['{@conditional.less_than}']
1736
1581
  - to: stopper
1737
1582
  conditions:
1738
- code: [590, 591, 596, 597, 598, 599]
1583
+ code: [590, 591, 596, 597, 598]
1739
1584
  match:
1740
1585
  - expected: true
1741
1586
  actual:
@@ -1786,19 +1631,6 @@ const getWorkflowYAML = (app, version) => {
1786
1631
  - to: signaler_proxyer
1787
1632
  conditions:
1788
1633
  code: 591
1789
- - to: signaler_retryer
1790
- conditions:
1791
- code: 599
1792
- match:
1793
- - expected: true
1794
- actual:
1795
- '@pipe':
1796
- - '@pipe':
1797
- - ['{signaler_cycle_hook.output.data.retryCount}']
1798
- - '@pipe':
1799
- - ['{trigger.output.data.maximumAttempts}', 5]
1800
- - ['{@conditional.nullish}']
1801
- - ['{@conditional.less_than}']
1802
1634
  signaler_collator:
1803
1635
  - to: signaler_collate_cycler
1804
1636
  signaler_childer:
@@ -2215,9 +2047,13 @@ const getWorkflowYAML = (app, version) => {
2215
2047
  - ['{@object.create}']
2216
2048
 
2217
2049
  collator_proxyer:
2218
- title: Invokes the activity flow and awaits the response
2219
- type: await
2220
- topic: ${app}.activity.execute
2050
+ title: Calls the activity function directly via worker topic
2051
+ type: worker
2052
+ topic:
2053
+ '@pipe':
2054
+ - ['{collator_trigger.output.data.items}', '{collator_cycle_hook.output.data.cur_index}']
2055
+ - ['{@array.get}', workflowTopic]
2056
+ - ['{@object.get}']
2221
2057
  input:
2222
2058
  schema:
2223
2059
  type: object
@@ -2327,23 +2163,6 @@ const getWorkflowYAML = (app, version) => {
2327
2163
  properties:
2328
2164
  response:
2329
2165
  type: any
2330
- $error:
2331
- type: object
2332
- properties:
2333
- code:
2334
- type: number
2335
- message:
2336
- type: string
2337
- stack:
2338
- type: string
2339
- done:
2340
- type: boolean
2341
- workflowId:
2342
- type: string
2343
- jc:
2344
- type: string
2345
- ju:
2346
- type: string
2347
2166
  job:
2348
2167
  maps:
2349
2168
  response[25]:
@@ -2352,46 +2171,44 @@ const getWorkflowYAML = (app, version) => {
2352
2171
  - ['{collator_cycle_hook.output.data.cur_index}']
2353
2172
  - '@pipe':
2354
2173
  - '@pipe':
2355
- - ['{$self.output.data.response}']
2174
+ - ['{$self.output.data.$error}', '{$self.output.metadata.err}']
2175
+ - ['{@logical.or}']
2356
2176
  - '@pipe':
2357
2177
  - '@pipe':
2358
2178
  - [type]
2359
2179
  - '@pipe':
2360
2180
  - ['proxy']
2361
2181
  - '@pipe':
2362
- - [data]
2182
+ - [$error]
2363
2183
  - '@pipe':
2364
- - ['{$self.output.data.response}']
2184
+ - ['{$self.output.data.$error}', '{$self.output.metadata.$error}']
2185
+ - ['{@conditional.nullish}']
2365
2186
  - '@pipe':
2366
2187
  - [ac]
2367
2188
  - '@pipe':
2368
- - ['{$job.metadata.jc}']
2189
+ - ['{$self.output.metadata.ac}']
2369
2190
  - '@pipe':
2370
2191
  - [au]
2371
2192
  - '@pipe':
2372
- - ['{@date.toISOXString}']
2373
- - '@pipe':
2374
- - [job_id]
2375
- - '@pipe':
2376
- - ['{$self.output.data.workflowId}']
2193
+ - ['{$self.output.metadata.au}']
2377
2194
  - ['{@object.create}']
2378
2195
  - '@pipe':
2379
2196
  - '@pipe':
2380
- - [$error]
2197
+ - [type]
2381
2198
  - '@pipe':
2382
- - ['{$self.output.data}']
2199
+ - ['proxy']
2383
2200
  - '@pipe':
2384
- - [ac]
2201
+ - [data]
2385
2202
  - '@pipe':
2386
- - ['{$job.metadata.jc}']
2203
+ - ['{$self.output.data.response}']
2387
2204
  - '@pipe':
2388
- - [au]
2205
+ - [ac]
2389
2206
  - '@pipe':
2390
- - ['{@date.toISOXString}']
2207
+ - ['{$self.output.metadata.ac}']
2391
2208
  - '@pipe':
2392
- - [job_id]
2209
+ - [au]
2393
2210
  - '@pipe':
2394
- - ['{$self.output.data.workflowId}']
2211
+ - ['{$self.output.metadata.au}']
2395
2212
  - ['{@object.create}']
2396
2213
  - ['{@conditional.ternary}']
2397
2214
  - ['{@object.create}']
@@ -2453,6 +2270,8 @@ const getWorkflowYAML = (app, version) => {
2453
2270
  - ['{collator_trigger.output.data.items}', '{collator_cycle_hook.output.data.cur_index}']
2454
2271
  - ['{@array.get}', code]
2455
2272
  - ['{@object.get}']
2273
+ collator_proxyer:
2274
+ - to: collator_cycler
2456
2275
 
2457
2276
  hooks:
2458
2277
  ${app}.wfs.signal:
@@ -2465,227 +2284,6 @@ const getWorkflowYAML = (app, version) => {
2465
2284
  - ['{@array.get}', signalId]
2466
2285
  - ['{@object.get}']
2467
2286
  actual: '{$self.hook.data.id}'
2468
-
2469
-
2470
-
2471
- ###################################################
2472
- # THE REENTRANT ACTIVITY FLOW #
2473
- # #
2474
- - subscribes: ${app}.activity.execute
2475
- publishes: ${app}.activity.executed
2476
-
2477
- expire:
2478
- '@pipe':
2479
- - '@pipe':
2480
- - ['{activity_trigger.output.data.startToCloseTimeout}']
2481
- - '@pipe':
2482
- - ['{activity_trigger.output.data.expire}', 1]
2483
- - ['{@conditional.nullish}']
2484
- - ['{@conditional.nullish}']
2485
-
2486
- input:
2487
- schema:
2488
- type: object
2489
- properties:
2490
- parentWorkflowId:
2491
- type: string
2492
- originJobId:
2493
- type: string
2494
- workflowId:
2495
- type: string
2496
- workflowTopic:
2497
- type: string
2498
- activityName:
2499
- type: string
2500
- arguments:
2501
- type: array
2502
- headers:
2503
- type: object
2504
- backoffCoefficient:
2505
- type: number
2506
- initialInterval:
2507
- type: number
2508
- maximumAttempts:
2509
- type: number
2510
- maximumInterval:
2511
- type: number
2512
- startToCloseTimeout:
2513
- type: number
2514
- expire:
2515
- type: number
2516
- output:
2517
- schema:
2518
- type: object
2519
- properties:
2520
- response:
2521
- type: any
2522
- done:
2523
- type: boolean
2524
- workflowId:
2525
- type: string
2526
- jc:
2527
- type: string
2528
- ju:
2529
- type: string
2530
-
2531
- activities:
2532
- activity_trigger:
2533
- title: Activity Flow Trigger
2534
- type: trigger
2535
- stats:
2536
- id: '{$self.input.data.workflowId}'
2537
- key: '{$self.input.data.parentWorkflowId}'
2538
- parent: '{$self.input.data.originJobId}'
2539
- adjacent: '{$self.input.data.parentWorkflowId}'
2540
- job:
2541
- maps:
2542
- workflowId: '{$self.input.data.workflowId}'
2543
-
2544
- activity_cycle_hook:
2545
- title: Activity Flow Pivot - Cycling Descendants Point Here
2546
- type: hook
2547
- cycle: true
2548
- output:
2549
- schema:
2550
- type: object
2551
- properties:
2552
- retryCount:
2553
- type: number
2554
- maps:
2555
- retryCount: 0
2556
-
2557
- activity_worker:
2558
- title: Activity Worker - Calls Activity Functions
2559
- type: worker
2560
- topic: '{activity_trigger.output.data.workflowTopic}'
2561
- input:
2562
- schema:
2563
- type: object
2564
- properties:
2565
- parentWorkflowId:
2566
- type: string
2567
- workflowId:
2568
- type: string
2569
- workflowTopic:
2570
- type: string
2571
- activityName:
2572
- type: string
2573
- arguments:
2574
- type: array
2575
- headers:
2576
- type: object
2577
- startToCloseTimeout:
2578
- type: number
2579
- maps:
2580
- parentWorkflowId: '{activity_trigger.output.data.parentWorkflowId}'
2581
- workflowId: '{activity_trigger.output.data.workflowId}'
2582
- workflowTopic: '{activity_trigger.output.data.workflowTopic}'
2583
- activityName: '{activity_trigger.output.data.activityName}'
2584
- arguments: '{activity_trigger.output.data.arguments}'
2585
- headers: '{activity_trigger.output.data.headers}'
2586
- startToCloseTimeout: '{activity_trigger.output.data.startToCloseTimeout}'
2587
- output:
2588
- schema:
2589
- type: object
2590
- properties:
2591
- response:
2592
- type: any
2593
- job:
2594
- maps:
2595
- response: '{$self.output.data.response}'
2596
-
2597
- activity_retryer:
2598
- title: Pauses for an exponentially-throttled amount of time after a 599 (retryable) error
2599
- type: hook
2600
- sleep:
2601
- '@pipe':
2602
- - '@pipe':
2603
- - '@pipe':
2604
- - ['{activity_trigger.output.data.backoffCoefficient}', 10]
2605
- - ['{@logical.or}', '{activity_cycle_hook.output.data.retryCount}']
2606
- - ['{@math.pow}']
2607
- - '@pipe':
2608
- - ['{activity_trigger.output.data.initialInterval}', 1]
2609
- - ['{@conditional.nullish}']
2610
- - ['{@math.multiply}']
2611
- - '@pipe':
2612
- - ['{activity_trigger.output.data.maximumInterval}', 120]
2613
- - ['{@math.min}']
2614
- - ['{@math.min}']
2615
-
2616
- activity_retry_cycler:
2617
- title: Cycles back to the activity_cycle_hook pivot, incrementing the \`retryCount\` (the exponential)
2618
- type: cycle
2619
- ancestor: activity_cycle_hook
2620
- input:
2621
- maps:
2622
- retryCount:
2623
- '@pipe':
2624
- - ['{activity_cycle_hook.output.data.retryCount}', 1]
2625
- - ['{@math.add}']
2626
-
2627
- activity_closer:
2628
- title: Marks the activity workflow as done
2629
- type: hook
2630
- job:
2631
- maps:
2632
- done: true
2633
- $error: '{activity_worker.output.data.$error}'
2634
- jc: '{$job.metadata.jc}'
2635
- ju:
2636
- '@pipe':
2637
- - ['{@date.toISOXString}']
2638
-
2639
- activity_stopper:
2640
- title: Stops the activity after retry count has been maxed
2641
- type: hook
2642
- job:
2643
- maps:
2644
- done: true
2645
- $error: '{activity_worker.output.data.$error}'
2646
- jc: '{$job.metadata.jc}'
2647
- ju:
2648
- '@pipe':
2649
- - ['{@date.toISOXString}']
2650
-
2651
- transitions:
2652
- activity_trigger:
2653
- - to: activity_cycle_hook
2654
- activity_cycle_hook:
2655
- - to: activity_worker
2656
- activity_worker:
2657
- - to: activity_closer
2658
- conditions:
2659
- code: [200, 598, 597, 596]
2660
- - to: activity_stopper
2661
- conditions:
2662
- code: 599
2663
- match:
2664
- - expected: true
2665
- actual:
2666
- '@pipe':
2667
- - '@pipe':
2668
- - ['{activity_cycle_hook.output.data.retryCount}']
2669
- - '@pipe':
2670
- - ['{activity_trigger.output.data.maximumAttempts}', 50]
2671
- - ['{@conditional.nullish}']
2672
- - ['{@conditional.greater_than_or_equal}']
2673
-
2674
- - to: activity_retryer
2675
- conditions:
2676
- code: 599
2677
- match:
2678
- - expected: true
2679
- actual:
2680
- '@pipe':
2681
- - '@pipe':
2682
- - ['{activity_cycle_hook.output.data.retryCount}']
2683
- - '@pipe':
2684
- - ['{activity_trigger.output.data.maximumAttempts}', 50]
2685
- - ['{@conditional.nullish}']
2686
- - ['{@conditional.less_than}']
2687
- activity_retryer:
2688
- - to: activity_retry_cycler
2689
2287
  `;
2690
2288
  };
2691
2289
  exports.getWorkflowYAML = getWorkflowYAML;
@@ -377,15 +377,13 @@ class WorkerService {
377
377
  //use code 599 as a proxy for all retryable errors
378
378
  // (basically anything not 596, 597, 598)
379
379
  return {
380
- status: stream_1.StreamStatus.SUCCESS,
380
+ status: stream_1.StreamStatus.ERROR,
381
381
  code: 599,
382
382
  metadata: { ...data.metadata },
383
383
  data: {
384
- $error: {
385
- message: err.message,
386
- stack: err.stack,
387
- code: enums_1.HMSH_CODE_DURABLE_RETRYABLE,
388
- },
384
+ message: err.message,
385
+ stack: err.stack,
386
+ code: enums_1.HMSH_CODE_DURABLE_RETRYABLE,
389
387
  },
390
388
  };
391
389
  }
@@ -607,15 +605,13 @@ class WorkerService {
607
605
  //use code 599 as a proxy for all retryable errors
608
606
  // (basically anything not 596, 597, 598)
609
607
  return {
610
- status: stream_1.StreamStatus.SUCCESS,
608
+ status: stream_1.StreamStatus.ERROR,
611
609
  code: enums_1.HMSH_CODE_DURABLE_RETRYABLE,
612
610
  metadata: { ...data.metadata },
613
611
  data: {
614
- $error: {
615
- message: err.message,
616
- stack: err.stack,
617
- timestamp: (0, utils_1.formatISODate)(new Date()),
618
- },
612
+ message: err.message,
613
+ stack: err.stack,
614
+ code: enums_1.HMSH_CODE_DURABLE_RETRYABLE,
619
615
  },
620
616
  };
621
617
  }
@@ -963,9 +959,24 @@ class WorkerService {
963
959
  }, telemetry_3.SpanStatusCode.ERROR, err.message);
964
960
  }
965
961
  isProcessing = true;
962
+ const errorCode = err.code || new errors_1.DurableRetryError(err.message).code;
963
+ if (errorCode === enums_1.HMSH_CODE_DURABLE_RETRYABLE) {
964
+ // Retryable errors use status: ERROR so the engine-level
965
+ // retry mechanism (handleRetry + _streamRetryConfig) kicks in
966
+ return withPatchMarkers({
967
+ status: stream_1.StreamStatus.ERROR,
968
+ code: errorCode,
969
+ metadata: { ...data.metadata },
970
+ data: {
971
+ message: err.message,
972
+ stack: err.stack,
973
+ code: errorCode,
974
+ },
975
+ });
976
+ }
966
977
  return withPatchMarkers({
967
978
  status: stream_1.StreamStatus.SUCCESS,
968
- code: err.code || new errors_1.DurableRetryError(err.message).code,
979
+ code: errorCode,
969
980
  metadata: { ...data.metadata },
970
981
  data: {
971
982
  $error: {
@@ -973,7 +984,7 @@ class WorkerService {
973
984
  type: err.name,
974
985
  name: err.name,
975
986
  stack: err.stack,
976
- code: err.code || new errors_1.DurableRetryError(err.message).code,
987
+ code: errorCode,
977
988
  },
978
989
  },
979
990
  });
@@ -585,14 +585,23 @@ class ConsumptionManager {
585
585
  // Extract retry policy with priority:
586
586
  // 1. Use message-level _streamRetryConfig (from database columns or previous retry)
587
587
  // 2. Fall back to router-level retry (from worker config)
588
- const retry = input._streamRetryConfig
588
+ const streamRetryConfig = input._streamRetryConfig;
589
+ const retry = streamRetryConfig
589
590
  ? {
590
- maximumAttempts: input._streamRetryConfig.max_retry_attempts,
591
- backoffCoefficient: input._streamRetryConfig.backoff_coefficient,
592
- maximumInterval: input._streamRetryConfig.maximum_interval_seconds,
591
+ maximumAttempts: streamRetryConfig.max_retry_attempts,
592
+ backoffCoefficient: streamRetryConfig.backoff_coefficient,
593
+ maximumInterval: streamRetryConfig.maximum_interval_seconds,
594
+ initialInterval: streamRetryConfig.initialInterval ?? input.data?.initialInterval ?? 1,
593
595
  }
594
596
  : this.retry;
595
- return await this.errorHandler.handleRetry(input, output, this.publishMessage.bind(this), retry);
597
+ return await this.errorHandler.handleRetry(input, output, this.publishMessage.bind(this), retry, (topic, delayMs) => {
598
+ // Schedule a targeted NOTIFY so the consumer wakes up
599
+ // when the visibility-delayed retry message becomes visible
600
+ if (typeof this.stream.scheduleStreamNotify === 'function') {
601
+ const streamKey = this.stream.mintKey(key_1.KeyType.STREAMS, { topic });
602
+ this.stream.scheduleStreamNotify(streamKey, delayMs);
603
+ }
604
+ });
596
605
  }
597
606
  else if (typeof output.metadata !== 'object') {
598
607
  output.metadata = { ...input.metadata, guid: (0, utils_1.guid)() };
@@ -4,5 +4,5 @@ export declare class ErrorHandler {
4
4
  structureUnhandledError(input: StreamData, err: Error): StreamDataResponse;
5
5
  structureUnacknowledgedError(input: StreamData): StreamDataResponse;
6
6
  structureError(input: StreamData, output: StreamDataResponse): StreamDataResponse;
7
- handleRetry(input: StreamData, output: StreamDataResponse, publishMessage: (topic: string, streamData: StreamData | StreamDataResponse) => Promise<string>, retry?: RetryPolicy): Promise<string>;
7
+ handleRetry(input: StreamData, output: StreamDataResponse, publishMessage: (topic: string, streamData: StreamData | StreamDataResponse) => Promise<string>, retry?: RetryPolicy, onRetryScheduled?: (topic: string, delayMs: number) => void): Promise<string>;
8
8
  }
@@ -14,16 +14,11 @@ class ErrorHandler {
14
14
  const maxInterval = typeof retry.maximumInterval === 'string'
15
15
  ? parseInt(retry.maximumInterval)
16
16
  : (retry.maximumInterval || 120);
17
- // Check if we can retry (next attempt would be attempt #tryCount+2, must be <= maxAttempts)
18
- // tryCount=0 is 1st attempt, tryCount=1 is 2nd attempt, etc.
19
- // So after tryCount, we've made (tryCount + 1) attempts
20
- // We can retry if (tryCount + 1) < maxAttempts
17
+ const initialIntervalS = retry.initialInterval || 1;
21
18
  if ((tryCount + 1) < maxAttempts) {
22
- // Exponential backoff: min(coefficient^(try+1), maxInterval)
23
- // First retry (after try=0): coefficient^1
24
- // Second retry (after try=1): coefficient^2, etc.
25
- const backoffSeconds = Math.min(Math.pow(backoffCoeff, tryCount + 1), maxInterval);
26
- return [true, backoffSeconds * 1000]; // Convert to milliseconds
19
+ // Exponential backoff: min(initialInterval * coefficient^(try+1), maxInterval)
20
+ const backoffSeconds = Math.min(initialIntervalS * Math.pow(backoffCoeff, tryCount + 1), maxInterval);
21
+ return [true, backoffSeconds * 1000];
27
22
  }
28
23
  return [false, 0];
29
24
  }
@@ -101,7 +96,7 @@ class ErrorHandler {
101
96
  data,
102
97
  };
103
98
  }
104
- async handleRetry(input, output, publishMessage, retry) {
99
+ async handleRetry(input, output, publishMessage, retry, onRetryScheduled) {
105
100
  const [shouldRetry, timeout] = this.shouldRetry(input, output, retry);
106
101
  if (shouldRetry) {
107
102
  // Only sleep if no retry (legacy behavior for backward compatibility)
@@ -126,7 +121,13 @@ class ErrorHandler {
126
121
  // Track retry attempt count in database
127
122
  const currentAttempt = input._retryAttempt || 0;
128
123
  newMessage._retryAttempt = currentAttempt + 1;
129
- return (await publishMessage(input.metadata.topic, newMessage));
124
+ const messageId = (await publishMessage(input.metadata.topic, newMessage));
125
+ // Schedule a targeted NOTIFY so the consumer wakes up when
126
+ // the visibility-delayed message becomes visible
127
+ if (retry && timeout > 0 && onRetryScheduled) {
128
+ onRetryScheduled(input.metadata.topic, timeout);
129
+ }
130
+ return messageId;
130
131
  }
131
132
  else {
132
133
  const structuredError = this.structureError(input, output);
@@ -15,6 +15,10 @@ export declare const GET_JOB_ATTRIBUTES = "\n SELECT symbol || dimension AS fie
15
15
  * Matches all activity jobs for the given workflow and extracts their input arguments.
16
16
  */
17
17
  export declare const GET_ACTIVITY_INPUTS = "\n SELECT j.key, ja.value\n FROM {schema}.jobs j\n JOIN {schema}.jobs_attributes ja ON ja.job_id = j.id\n WHERE j.key LIKE $1\n AND ja.symbol = $2 AND ja.dimension = $3\n";
18
+ /**
19
+ * Fetch activity inputs from worker_streams for direct worker proxyer activities.
20
+ */
21
+ export declare const GET_PROXYER_STREAM_INPUTS = "\n SELECT message\n FROM {schema}.worker_streams\n WHERE jid = $1\n AND aid IN ('proxyer', 'collator_proxyer', 'signaler_proxyer')\n ORDER BY created_at, id\n";
18
22
  /**
19
23
  * Fetch all worker stream messages for a job AND its child activities.
20
24
  * Child activity jobs use the pattern: -{parentJobId}-$activityName-N
@@ -4,7 +4,7 @@
4
4
  * These queries support the exporter's input enrichment and direct query features.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.buildChildWorkflowInputsQuery = exports.GET_STREAM_HISTORY_BY_JID_AND_AID = exports.GET_STREAM_HISTORY_BY_JID_AND_TYPE = exports.GET_STREAM_HISTORY_BY_JID = exports.GET_ACTIVITY_INPUTS = exports.GET_JOB_ATTRIBUTES = exports.GET_JOB_BY_KEY = void 0;
7
+ exports.buildChildWorkflowInputsQuery = exports.GET_STREAM_HISTORY_BY_JID_AND_AID = exports.GET_STREAM_HISTORY_BY_JID_AND_TYPE = exports.GET_STREAM_HISTORY_BY_JID = exports.GET_PROXYER_STREAM_INPUTS = exports.GET_ACTIVITY_INPUTS = exports.GET_JOB_ATTRIBUTES = exports.GET_JOB_BY_KEY = void 0;
8
8
  /**
9
9
  * Fetch job record by key.
10
10
  */
@@ -34,6 +34,16 @@ exports.GET_ACTIVITY_INPUTS = `
34
34
  WHERE j.key LIKE $1
35
35
  AND ja.symbol = $2 AND ja.dimension = $3
36
36
  `;
37
+ /**
38
+ * Fetch activity inputs from worker_streams for direct worker proxyer activities.
39
+ */
40
+ exports.GET_PROXYER_STREAM_INPUTS = `
41
+ SELECT message
42
+ FROM {schema}.worker_streams
43
+ WHERE jid = $1
44
+ AND aid IN ('proxyer', 'collator_proxyer', 'signaler_proxyer')
45
+ ORDER BY created_at, id
46
+ `;
37
47
  /**
38
48
  * Fetch all worker stream messages for a job AND its child activities.
39
49
  * Child activity jobs use the pattern: -{parentJobId}-$activityName-N
@@ -1442,6 +1442,30 @@ class PostgresStoreService extends __1.StoreService {
1442
1442
  // Skip unparseable values
1443
1443
  }
1444
1444
  }
1445
+ // If no results from legacy approach, try direct worker approach:
1446
+ // extract arguments from proxyer messages in worker_streams
1447
+ if (byNameIndex.size === 0) {
1448
+ const { GET_PROXYER_STREAM_INPUTS } = await Promise.resolve().then(() => __importStar(require('./exporter-sql')));
1449
+ const streamSql = GET_PROXYER_STREAM_INPUTS.replace(/{schema}/g, schemaName);
1450
+ const streamResult = await this.pgClient.query(streamSql, [workflowId]);
1451
+ for (const row of streamResult.rows) {
1452
+ try {
1453
+ const msg = typeof row.message === 'string' ? JSON.parse(row.message) : row.message;
1454
+ const data = msg?.data;
1455
+ if (data?.activityName && data?.arguments) {
1456
+ const activityName = data.activityName;
1457
+ const wfId = data.workflowId || '';
1458
+ const idxMatch = wfId.match(/-(\d+)$/);
1459
+ const execIndex = idxMatch ? idxMatch[1] : '0';
1460
+ byNameIndex.set(`${activityName}:${execIndex}`, data.arguments);
1461
+ byJobId.set(wfId, data.arguments);
1462
+ }
1463
+ }
1464
+ catch {
1465
+ // Skip unparseable messages
1466
+ }
1467
+ }
1468
+ }
1445
1469
  return { byJobId, byNameIndex };
1446
1470
  }
1447
1471
  /**
@@ -58,6 +58,12 @@ declare class PostgresStreamService extends StreamService<PostgresClientType & P
58
58
  * added to the transaction for atomic execution.
59
59
  */
60
60
  publishMessages(streamName: string, messages: string[], options?: PublishMessageConfig): Promise<string[] | ProviderTransaction>;
61
+ /**
62
+ * Schedule a NOTIFY for a worker stream after a delay. Used to wake up
63
+ * consumers when a visibility-delayed retry message becomes visible,
64
+ * avoiding the need to wait for the scout's fallback poll.
65
+ */
66
+ scheduleStreamNotify(streamName: string, delayMs: number): void;
61
67
  _publishMessages(streamName: string, messages: string[], options?: PublishMessageConfig): {
62
68
  sql: string;
63
69
  params: any[];
@@ -174,6 +174,31 @@ class PostgresStreamService extends index_1.StreamService {
174
174
  const target = this.resolveStreamTarget(streamName);
175
175
  return Messages.publishMessages(this.streamClient, target.tableName, target.streamName, target.isEngine, messages, options, this.logger);
176
176
  }
177
+ /**
178
+ * Schedule a NOTIFY for a worker stream after a delay. Used to wake up
179
+ * consumers when a visibility-delayed retry message becomes visible,
180
+ * avoiding the need to wait for the scout's fallback poll.
181
+ */
182
+ scheduleStreamNotify(streamName, delayMs) {
183
+ const target = this.resolveStreamTarget(streamName);
184
+ const prefix = target.isEngine ? 'eng_' : 'wrk_';
185
+ let channelName = `${prefix}${target.streamName}`;
186
+ if (channelName.length > 63) {
187
+ channelName = channelName.substring(0, 63);
188
+ }
189
+ const payload = JSON.stringify({
190
+ stream_name: target.streamName,
191
+ table_type: target.isEngine ? 'engine' : 'worker',
192
+ });
193
+ setTimeout(async () => {
194
+ try {
195
+ await this.streamClient.query(`SELECT pg_notify($1, $2)`, [channelName, payload]);
196
+ }
197
+ catch {
198
+ // Best-effort; the scout fallback will pick it up
199
+ }
200
+ }, delayMs);
201
+ }
177
202
  _publishMessages(streamName, messages, options) {
178
203
  const target = this.resolveStreamTarget(streamName);
179
204
  return Messages.buildPublishSQL(target.tableName, target.streamName, target.isEngine, messages, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.14.7",
3
+ "version": "0.14.8",
4
4
  "description": "Durable Workflow",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",