@outputai/core 0.5.3-next.bdf47aa.0 → 0.6.1-next.1f47248.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.
Files changed (51) hide show
  1. package/package.json +2 -2
  2. package/src/activity_integration/context.d.ts +5 -9
  3. package/src/activity_integration/context.js +5 -4
  4. package/src/activity_integration/context.spec.js +10 -15
  5. package/src/activity_integration/events.d.ts +2 -4
  6. package/src/activity_integration/events.js +8 -3
  7. package/src/activity_integration/events.spec.js +58 -29
  8. package/src/bus.js +18 -9
  9. package/src/bus.spec.js +30 -0
  10. package/src/hooks/index.d.ts +112 -58
  11. package/src/hooks/index.js +15 -12
  12. package/src/hooks/index.spec.js +60 -32
  13. package/src/interface/workflow.js +19 -35
  14. package/src/interface/workflow.spec.js +104 -15
  15. package/src/internal_activities/index.js +3 -3
  16. package/src/internal_activities/index.spec.js +31 -1
  17. package/src/internal_utils/temporal_context.js +12 -0
  18. package/src/internal_utils/temporal_context.spec.ts +83 -0
  19. package/src/internal_utils/trace_info.js +21 -0
  20. package/src/internal_utils/trace_info.spec.js +47 -0
  21. package/src/internal_utils/workflow_context.js +29 -0
  22. package/src/internal_utils/workflow_context.spec.js +46 -0
  23. package/src/logger/development.js +61 -0
  24. package/src/logger/development.spec.js +70 -0
  25. package/src/logger/index.js +14 -0
  26. package/src/logger/index.spec.js +27 -0
  27. package/src/logger/production.js +15 -0
  28. package/src/logger/production.spec.js +52 -0
  29. package/src/tracing/internal_interface.js +4 -4
  30. package/src/tracing/processors/local/index.js +21 -26
  31. package/src/tracing/processors/local/index.spec.js +39 -45
  32. package/src/tracing/processors/s3/index.js +13 -23
  33. package/src/tracing/processors/s3/index.spec.js +33 -26
  34. package/src/tracing/trace_attribute.js +0 -1
  35. package/src/tracing/trace_engine.js +8 -12
  36. package/src/tracing/trace_engine.spec.js +31 -27
  37. package/src/worker/configs.js +2 -0
  38. package/src/worker/configs.spec.js +25 -0
  39. package/src/worker/index.js +4 -1
  40. package/src/worker/index.spec.js +4 -0
  41. package/src/worker/interceptors/activity.js +31 -29
  42. package/src/worker/interceptors/activity.spec.js +58 -26
  43. package/src/worker/interceptors/workflow.js +7 -2
  44. package/src/worker/interceptors/workflow.spec.js +42 -6
  45. package/src/worker/log_hooks.js +35 -46
  46. package/src/worker/log_hooks.spec.js +43 -46
  47. package/src/worker/setup_telemetry.js +19 -0
  48. package/src/worker/setup_telemetry.spec.js +80 -0
  49. package/src/worker/sinks.js +24 -24
  50. package/src/interface/workflow_context.js +0 -33
  51. package/src/logger.js +0 -73
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outputai/core",
3
- "version": "0.5.3-next.bdf47aa.0",
3
+ "version": "0.6.1-next.1f47248.0",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "exports": {
@@ -65,7 +65,7 @@
65
65
  "#bus": "./src/bus.js",
66
66
  "#consts": "./src/consts.js",
67
67
  "#errors": "./src/errors.js",
68
- "#logger": "./src/logger.js",
68
+ "#logger": "./src/logger/index.js",
69
69
  "#utils": "./src/utils/index.js",
70
70
  "#internal_utils/*": "./src/internal_utils/*.js",
71
71
  "#tracing": "./src/tracing/internal_interface.js",
@@ -1,16 +1,12 @@
1
+ import type { Info } from '@temporalio/activity';
1
2
  /**
2
3
  * Context returned by {@link getContext} when running inside a Temporal Activity (step or evaluator).
3
4
  */
4
5
  export type Context = {
5
- /** Information about the current workflow execution */
6
- workflow: {
7
- /** Temporal's workflow execution id */
8
- id: string;
9
- /** Workflow name (Temporal's workflow "type" value) */
10
- name: string;
11
- /** Path of the workflow file */
12
- filename: string;
13
- }
6
+ /** Temporal info about the current activity */
7
+ activityInfo: Info,
8
+ /** Path of the workflow file */
9
+ workflowFilename: string
14
10
  };
15
11
 
16
12
  /**
@@ -7,11 +7,12 @@ import { Storage } from '#async_storage';
7
7
  */
8
8
  export const getExecutionContext = () => {
9
9
  const ctx = Storage.load();
10
-
11
- if ( !ctx?.executionContext || !ctx?.workflowFilename ) {
10
+ if ( !ctx ) {
12
11
  return null;
13
12
  }
14
13
 
15
- const { workflowId: id, workflowName: name } = ctx.executionContext;
16
- return { workflow: { id, name, filename: ctx.workflowFilename } };
14
+ return {
15
+ workflowFilename: ctx.workflowFilename,
16
+ activityInfo: ctx.activityInfo
17
+ };
17
18
  };
@@ -17,26 +17,21 @@ describe( 'getExecutionContext', () => {
17
17
  expect( getExecutionContext() ).toBeNull();
18
18
  } );
19
19
 
20
- it( 'returns null when executionContext is missing', async () => {
21
- loadMock.mockReturnValue( { workflowFilename: '/workflows/foo.js' } );
22
- const { getExecutionContext } = await import( './index.js' );
23
- expect( getExecutionContext() ).toBeNull();
24
- } );
25
-
26
- it( 'returns null when workflowFilename is missing', async () => {
27
- loadMock.mockReturnValue( { executionContext: { workflowId: 'wf-1', workflowName: 'myWorkflow' } } );
28
- const { getExecutionContext } = await import( './index.js' );
29
- expect( getExecutionContext() ).toBeNull();
30
- } );
31
-
32
- it( 'returns workflow context when storage has full context', async () => {
20
+ it( 'returns activity execution context from storage', async () => {
21
+ const activityInfo = {
22
+ activityId: 'activity-1',
23
+ activityType: 'myActivity',
24
+ workflowExecution: { workflowId: 'wf-1', runId: 'run-1' },
25
+ workflowType: 'myWorkflow'
26
+ };
33
27
  loadMock.mockReturnValue( {
34
- executionContext: { workflowId: 'wf-1', workflowName: 'myWorkflow' },
28
+ activityInfo,
35
29
  workflowFilename: '/workflows/myWorkflow.js'
36
30
  } );
37
31
  const { getExecutionContext } = await import( './index.js' );
38
32
  expect( getExecutionContext() ).toEqual( {
39
- workflow: { id: 'wf-1', name: 'myWorkflow', filename: '/workflows/myWorkflow.js' }
33
+ activityInfo,
34
+ workflowFilename: '/workflows/myWorkflow.js'
40
35
  } );
41
36
  } );
42
37
  } );
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * Emits a custom event on the in-process message bus.
3
3
  *
4
- * The framework automatically attaches `workflowId`, `runId`, and `activityId`
5
- * (pulled from `executionContext`) onto every emitted payload, so consumer
6
- * subscribers registered via `on(...)` always receive those identifiers
7
- * alongside whatever custom fields the emitter supplies.
4
+ * When called inside an Output activity context, the framework automatically
5
+ * attaches `activityInfo`, `workflowDetails`, and `outputActivityKind` onto the emitted payload.
8
6
  *
9
7
  * @param eventName - The name of the event to emit
10
8
  * @param payload - An optional payload to send to the event
@@ -4,7 +4,12 @@ import { Storage } from '#async_storage';
4
4
  export const emitEvent = ( eventName, payload ) => {
5
5
  const ctx = Storage.load();
6
6
 
7
- const { executionContext, parentId: activityId } = ctx ?? {};
8
- const { workflowId, runId } = executionContext ?? {};
9
- messageBus.emit( `external:${eventName}`, { ...payload ?? {}, workflowId, runId, activityId } );
7
+ messageBus.emit( `external:${eventName}`, {
8
+ ...payload ?? {},
9
+ ...( ctx && {
10
+ activityInfo: ctx.activityInfo,
11
+ workflowDetails: ctx.workflowDetails,
12
+ outputActivityKind: ctx.outputActivityKind
13
+ } )
14
+ } );
10
15
  };
@@ -21,69 +21,98 @@ describe( 'emitEvent', () => {
21
21
  vi.clearAllMocks();
22
22
  } );
23
23
 
24
- it( 'forwards workflowId, runId, and activityId from executionContext', () => {
24
+ it( 'forwards activityInfo, workflowDetails, and outputActivityKind from storage', () => {
25
+ const activityInfo = {
26
+ activityId: 'act-1',
27
+ activityType: 'step',
28
+ workflowExecution: { workflowId: 'wf-1', runId: 'run-1' },
29
+ workflowType: 'workflow'
30
+ };
31
+ const workflowDetails = {
32
+ workflowId: 'wf-1',
33
+ runId: 'run-1',
34
+ workflowType: 'workflow'
35
+ };
25
36
  loadMock.mockReturnValue( {
26
- executionContext: { workflowId: 'wf-1', runId: 'run-1' },
27
- parentId: 'act-1'
37
+ activityInfo,
38
+ workflowDetails,
39
+ outputActivityKind: 'step'
28
40
  } );
29
41
 
30
42
  emitEvent( 'cost:llm:request', { modelId: 'gpt-4o' } );
31
43
 
32
44
  expect( emitMock ).toHaveBeenCalledWith( 'external:cost:llm:request', expect.objectContaining( {
33
- workflowId: 'wf-1',
34
- runId: 'run-1',
35
- activityId: 'act-1',
45
+ activityInfo,
46
+ workflowDetails,
47
+ outputActivityKind: 'step',
36
48
  modelId: 'gpt-4o'
37
49
  } ) );
38
50
  } );
39
51
 
40
- it( 'handles missing executionContext gracefully', () => {
52
+ it( 'emits payload without context when storage is missing', () => {
41
53
  loadMock.mockReturnValue( undefined );
42
54
 
43
55
  emitEvent( 'foo:bar', { x: 1 } );
44
56
 
45
- expect( emitMock ).toHaveBeenCalledWith( 'external:foo:bar', expect.objectContaining( {
46
- workflowId: undefined,
47
- runId: undefined,
48
- activityId: undefined,
49
- x: 1
50
- } ) );
57
+ expect( emitMock ).toHaveBeenCalledWith( 'external:foo:bar', { x: 1 } );
51
58
  } );
52
59
 
53
60
  it( 'handles missing payload', () => {
61
+ const activityInfo = {
62
+ activityId: 'act-2',
63
+ activityType: 'step',
64
+ workflowExecution: { workflowId: 'wf-2', runId: 'run-2' },
65
+ workflowType: 'workflow'
66
+ };
67
+ const workflowDetails = {
68
+ workflowId: 'wf-2',
69
+ runId: 'run-2',
70
+ workflowType: 'workflow'
71
+ };
54
72
  loadMock.mockReturnValue( {
55
- executionContext: { workflowId: 'wf-2', runId: 'run-2' },
56
- parentId: 'act-2'
73
+ activityInfo,
74
+ workflowDetails,
75
+ outputActivityKind: 'step'
57
76
  } );
58
77
 
59
78
  emitEvent( 'lifecycle:start' );
60
79
 
61
80
  expect( emitMock ).toHaveBeenCalledWith( 'external:lifecycle:start', expect.objectContaining( {
62
- workflowId: 'wf-2',
63
- runId: 'run-2',
64
- activityId: 'act-2'
81
+ activityInfo,
82
+ workflowDetails,
83
+ outputActivityKind: 'step'
65
84
  } ) );
66
85
  } );
67
86
 
68
- it( 'does not let payload override workflowId / runId / activityId', () => {
87
+ it( 'does not let payload override activityInfo, workflowDetails, or outputActivityKind', () => {
88
+ const activityInfo = {
89
+ activityId: 'act-3',
90
+ activityType: 'step',
91
+ workflowExecution: { workflowId: 'wf-3', runId: 'run-3' },
92
+ workflowType: 'workflow'
93
+ };
94
+ const workflowDetails = {
95
+ workflowId: 'wf-3',
96
+ runId: 'run-3',
97
+ workflowType: 'workflow'
98
+ };
69
99
  loadMock.mockReturnValue( {
70
- executionContext: { workflowId: 'wf-3', runId: 'run-3' },
71
- parentId: 'act-3'
100
+ activityInfo,
101
+ workflowDetails,
102
+ outputActivityKind: 'step'
72
103
  } );
73
104
 
74
105
  emitEvent( 'cost:http:request', {
75
- workflowId: 'should-be-overridden',
76
- runId: 'should-be-overridden',
77
- activityId: 'should-be-overridden',
106
+ activityInfo: { activityId: 'should-be-overridden' },
107
+ workflowDetails: { workflowId: 'should-be-overridden' },
108
+ outputActivityKind: 'should-be-overridden',
78
109
  url: 'https://example.com'
79
110
  } );
80
111
 
81
- // Context fields are spread after the payload, so caller-supplied
82
- // workflowId / runId / activityId cannot escape the executionContext.
83
112
  expect( emitMock ).toHaveBeenCalledWith( 'external:cost:http:request', expect.objectContaining( {
84
- workflowId: 'wf-3',
85
- runId: 'run-3',
86
- activityId: 'act-3',
113
+ activityInfo,
114
+ workflowDetails,
115
+ outputActivityKind: 'step',
87
116
  url: 'https://example.com'
88
117
  } ) );
89
118
  } );
package/src/bus.js CHANGED
@@ -3,19 +3,28 @@ import { randomUUID } from 'node:crypto';
3
3
  import { isPlainObject } from '#utils';
4
4
 
5
5
  const emitter = new EventEmitter();
6
- const originalEmit = emitter.emit.bind( emitter );
7
-
8
- const attachEventId = payload => ( { ...payload, eventId: payload.eventId ?? randomUUID() } );
9
6
 
10
7
  /**
11
8
  * Every object payload emitted through `messageBus` is stamped with a UUID v4 `eventId`.
12
9
  */
13
- emitter.emit = ( event, ...args ) => {
14
- const [ payload, ...rest ] = args;
15
- if ( !isPlainObject( payload ) ) {
16
- return originalEmit( event, ...args );
10
+ emitter.emit = new Proxy( emitter.emit, {
11
+ apply( target, thisArg, args ) {
12
+ const [ eventName, payload, ...extras ] = args;
13
+ const newArguments = [];
14
+
15
+ // do now push arguments that dont exist. if payload is undefined with len=1, means it was never defined
16
+ // if payload is undefined with len>1, means user passed 'undefined' as argument
17
+ if ( args.length > 1 ) {
18
+ newArguments.push( isPlainObject( payload ) ? {
19
+ eventId: randomUUID(),
20
+ eventDate: Date.now(),
21
+ ...payload
22
+ } : payload );
23
+ newArguments.push( ...extras );
24
+ }
25
+
26
+ return Reflect.apply( target, thisArg, [ eventName, ...newArguments ] );
17
27
  }
18
- return originalEmit( event, attachEventId( payload ), ...rest );
19
- };
28
+ } );
20
29
 
21
30
  export const messageBus = emitter;
package/src/bus.spec.js CHANGED
@@ -47,6 +47,35 @@ describe( 'messageBus', () => {
47
47
  } ) );
48
48
  } );
49
49
 
50
+ it( 'stamps eventDate on every object payload', () => {
51
+ const handler = vi.fn();
52
+ const now = new Date( '2026-06-02T12:00:00.000Z' );
53
+ vi.useFakeTimers();
54
+ vi.setSystemTime( now );
55
+ messageBus.on( 'test:event', handler );
56
+
57
+ messageBus.emit( 'test:event', { foo: 'bar' } );
58
+
59
+ expect( handler ).toHaveBeenCalledWith( expect.objectContaining( {
60
+ eventDate: now.getTime(),
61
+ foo: 'bar'
62
+ } ) );
63
+
64
+ vi.useRealTimers();
65
+ } );
66
+
67
+ it( 'preserves a caller-supplied eventDate', () => {
68
+ const handler = vi.fn();
69
+ messageBus.on( 'test:event', handler );
70
+
71
+ messageBus.emit( 'test:event', { eventDate: 1234, foo: 'bar' } );
72
+
73
+ expect( handler ).toHaveBeenCalledWith( expect.objectContaining( {
74
+ eventDate: 1234,
75
+ foo: 'bar'
76
+ } ) );
77
+ } );
78
+
50
79
  it( 'does not mutate the caller-supplied payload object', () => {
51
80
  const handler = vi.fn();
52
81
  messageBus.on( 'test:event', handler );
@@ -55,6 +84,7 @@ describe( 'messageBus', () => {
55
84
  messageBus.emit( 'test:event', payload );
56
85
 
57
86
  expect( payload ).not.toHaveProperty( 'eventId' );
87
+ expect( payload ).not.toHaveProperty( 'eventDate' );
58
88
  } );
59
89
  } );
60
90
 
@@ -1,63 +1,130 @@
1
+ import type { Info } from '@temporalio/activity';
2
+
3
+ export interface WorkflowDetails {
4
+ /**
5
+ * ID of the Workflow, this can be set by the client during Workflow creation.
6
+ * A single Workflow may run multiple times e.g. when scheduled with cron.
7
+ */
8
+ workflowId: string;
9
+ /**
10
+ * ID of a single Workflow run
11
+ */
12
+ runId: string;
13
+ /**
14
+ * Workflow function's name
15
+ */
16
+ workflowType: string;
17
+ /**
18
+ * Parent Workflow info (present if this is a Child Workflow)
19
+ */
20
+ parent?: {
21
+ /**
22
+ * ID of the Workflow, this can be set by the client during Workflow creation.
23
+ * A single Workflow may run multiple times e.g. when scheduled with cron.
24
+ */
25
+ workflowId: string;
26
+ /**
27
+ * ID of a single Workflow run
28
+ */
29
+ runId: string;
30
+ /**
31
+ * Namespace this Workflow is executing in
32
+ */
33
+ namespace: string;
34
+ };
35
+ /**
36
+ * The root workflow execution, defined as follows:
37
+ * 1. A workflow without a parent workflow is its own root workflow.
38
+ * 2. A workflow with a parent workflow has the same root workflow as
39
+ * its parent.
40
+ *
41
+ * When there is no parent workflow, i.e., the workflow is its own root workflow,
42
+ * this field is `undefined`.
43
+ *
44
+ * Note that Continue-as-New (or reset) propagates the workflow parentage relationship,
45
+ * and therefore, whether the new workflow has the same root workflow as the original one
46
+ * depends on whether it had a parent.
47
+ *
48
+ */
49
+ root?: {
50
+ /**
51
+ * ID of the Workflow, this can be set by the client during Workflow creation.
52
+ * A single Workflow may run multiple times e.g. when scheduled with cron.
53
+ */
54
+ workflowId: string;
55
+ /**
56
+ * ID of a single Workflow run
57
+ */
58
+ runId: string;
59
+ };
60
+ /**
61
+ * Run Id of the first Run in this Execution Chain
62
+ */
63
+ firstExecutionRunId: string;
64
+ /**
65
+ * The last Run Id in this Execution Chain
66
+ */
67
+ continuedFromExecutionRunId?: string;
68
+ /**
69
+ * Time at which this [Workflow Execution Chain](https://docs.temporal.io/workflows#workflow-execution-chain) was started
70
+ */
71
+ startTime: number;
72
+ /**
73
+ * Time at which the current Workflow Run started
74
+ */
75
+ runStartTime: number;
76
+ /**
77
+ * Starts at 1 and increments for every retry if there is a `retryPolicy`
78
+ */
79
+ attempt: number;
80
+ }
81
+
1
82
  /**
2
- * Payload passed to the onError handler when a workflow, activity or runtime error occurs.
83
+ * Payload passed to the onError() handler when a workflow, activity or runtime error occurs.
3
84
  */
4
85
  export interface ErrorHookPayload {
5
86
  /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
6
87
  eventId: string;
7
88
  /** Origin of the error: workflow execution, activity execution, or runtime. */
8
89
  source: 'workflow' | 'activity' | 'runtime';
9
- /** Name of the workflow, when the error is scoped to a workflow or activity. */
10
- workflowName?: string;
11
- /** Name of the activity, when the error is from an activity. */
12
- activityName?: string;
90
+ /** Information about the current workflow execution */
91
+ workflowDetails?: WorkflowDetails;
92
+ /** Temporal's activityInfo(). If source is activity */
93
+ activityInfo?: Info;
94
+ /** Output component kind for the activity, e.g. step, evaluator, or internal_step. */
95
+ outputActivityKind?: string;
13
96
  /** The error thrown. */
14
97
  error: Error;
15
98
  }
16
99
 
17
100
  /**
18
- * Payload passed to the onWorkflowStart handler when a workflow run begins.
101
+ * Payload passed to the onWorkflowStart() handler when a workflow run begins.
19
102
  */
20
103
  export interface WorkflowStartHookPayload {
21
104
  /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
22
105
  eventId: string;
23
- /** Workflow id (stable across retries / continue-as-new). */
24
- id: string;
25
- /** Temporal run id for the current execution attempt. */
26
- runId: string;
27
- /** Name of the workflow. */
28
- name: string;
106
+ /** Information about the current workflow execution */
107
+ workflowDetails: WorkflowDetails;
29
108
  }
30
109
 
31
110
  /**
32
- * Payload passed to the onWorkflowEnd handler when a workflow run completes successfully.
111
+ * Payload passed to the onWorkflowEnd() handler when a workflow run completes successfully.
33
112
  */
34
113
  export interface WorkflowEndHookPayload {
35
114
  /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
36
115
  eventId: string;
37
- /** Workflow id (stable across retries / continue-as-new). */
38
- id: string;
39
- /** Temporal run id for the current execution attempt. */
40
- runId: string;
41
- /** Name of the workflow. */
42
- name: string;
43
- /** Duration of the workflow run in milliseconds. */
44
- duration: number;
116
+ /** Information about the current workflow execution */
117
+ workflowDetails: WorkflowDetails;
45
118
  }
46
119
 
47
120
  /**
48
- * Payload passed to the onWorkflowError handler when a workflow run fails.
121
+ * Payload passed to the onWorkflowError() handler when a workflow run fails.
49
122
  */
50
123
  export interface WorkflowErrorHookPayload {
51
124
  /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
52
125
  eventId: string;
53
- /** Workflow id (stable across retries / continue-as-new). */
54
- id: string;
55
- /** Temporal run id for the current execution attempt. */
56
- runId: string;
57
- /** Name of the workflow. */
58
- name: string;
59
- /** Elapsed time before failure in milliseconds. */
60
- duration: number;
126
+ /** Information about the current workflow execution */
127
+ workflowDetails: WorkflowDetails;
61
128
  /** The error thrown. */
62
129
  error: Error;
63
130
  }
@@ -105,42 +172,29 @@ export declare function onWorkflowEnd( handler: ( payload: WorkflowEndHookPayloa
105
172
  export declare function onWorkflowError( handler: ( payload: WorkflowErrorHookPayload ) => void ): void;
106
173
 
107
174
  /**
108
- * Payload broadcast on the `http:request` event for every HTTP call issued
109
- * through `@outputai/http`'s `fetch`. Fires for success, non-2xx, and network
110
- * failure paths — `cost:http:request` continues to fire only when the consumer
111
- * has attached a cost via `addRequestCost`.
112
- *
113
- * The framework auto-attaches `workflowId`, `runId`, and `activityId` onto the
114
- * payload before broadcast, so consumers receive those identifiers in addition
115
- * to the fields listed here.
175
+ * Framework-managed envelope added to payloads passed to on() handlers.
116
176
  */
117
- export interface HttpRequestHookPayload {
177
+ export interface OnHookEnvelope {
118
178
  /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
119
179
  eventId: string;
120
- /** Workflow id (stable across retries / continue-as-new). */
121
- workflowId: string;
122
- /** Temporal run id for the current execution attempt. */
123
- runId: string;
124
- /** Activity / step id, when emitted from inside a step. */
125
- activityId?: string;
126
- /** UUID generated per request inside `@outputai/http`. */
127
- requestId: string;
128
- /** HTTP method (uppercase). */
129
- method: string;
130
- /** Absolute request URL. */
131
- url: string;
132
- /** HTTP status code; undefined on network failure. */
133
- status?: number;
134
- /** Elapsed time from request issuance to response (or failure), in milliseconds. */
135
- durationMs: number;
136
- /** Outcome bucket: `success` (2xx-3xx), `error` (status >= 400), `failure` (DNS / timeout / abort). */
137
- outcome: 'success' | 'error' | 'failure';
180
+ /** Information about the current workflow execution */
181
+ workflowDetails: WorkflowDetails;
182
+ /** Temporal's activityInfo(). */
183
+ activityInfo: Info;
184
+ /** Output component kind for the activity, e.g. step, evaluator, or internal_step. */
185
+ outputActivityKind?: string;
138
186
  }
139
187
 
188
+ export type OnHookPayload<TAttributes extends Record<string, unknown> = Record<string, unknown>> =
189
+ OnHookEnvelope & TAttributes;
190
+
140
191
  /**
141
192
  * Register a handler to be invoked when a given event happens
142
193
  *
143
194
  * @param eventName - The name of the event to subscribe
144
195
  * @param handler - Function called with the event payload
145
196
  */
146
- export declare function on( eventName: string, handler: ( payload?: object ) => void ): void;
197
+ export declare function on<TAttributes extends Record<string, unknown> = Record<string, unknown>>(
198
+ eventName: string,
199
+ handler: ( payload: OnHookPayload<TAttributes> ) => void
200
+ ): void;
@@ -21,29 +21,32 @@ const safeInvoke = async ( fn, args, hookName ) => {
21
21
 
22
22
  /** Triggers on any errors: workflow, activity and runtime */
23
23
  export const onError = handler => {
24
- messageBus.on( BusEventType.ACTIVITY_ERROR, async ( { eventId, id, name, workflowId, workflowName, error } ) =>
25
- safeInvoke( handler, { eventId, source: 'activity', activityId: id, activityName: name, workflowId, workflowName, error }, 'onError' ) );
26
- messageBus.on( BusEventType.WORKFLOW_ERROR, async ( { eventId, id, name, error } ) =>
27
- safeInvoke( handler, { eventId, source: 'workflow', workflowId: id, workflowName: name, error }, 'onError' ) );
28
- messageBus.on( BusEventType.RUNTIME_ERROR, async ( { eventId, error } ) =>
29
- safeInvoke( handler, { eventId, source: 'runtime', error }, 'onError' ) );
24
+ messageBus.on( BusEventType.ACTIVITY_ERROR, async payload =>
25
+ safeInvoke( handler, { source: 'activity', ...payload }, 'onError' ) );
26
+ messageBus.on( BusEventType.WORKFLOW_ERROR, async payload =>
27
+ safeInvoke( handler, { source: 'workflow', ...payload }, 'onError' ) );
28
+ messageBus.on( BusEventType.RUNTIME_ERROR, async payload =>
29
+ safeInvoke( handler, { source: 'runtime', ...payload }, 'onError' ) );
30
30
  };
31
31
 
32
32
  /** Listen to worker before start events */
33
33
  export const onBeforeWorkerStart = handler => messageBus.on( BusEventType.WORKER_BEFORE_START, () =>
34
34
  safeInvoke( handler, undefined, 'onBeforeWorkerStart' ) );
35
35
 
36
+ /** Catalog workflow events should not be emitted */
37
+ const shouldEmitWorkflowEvent = workflowDetails => WORKFLOW_CATALOG !== workflowDetails.workflowType;
38
+
36
39
  /** Listen to workflow start events, excludes catalog workflow */
37
- export const onWorkflowStart = handler => messageBus.on( BusEventType.WORKFLOW_START, ( { eventId, id, runId, name } ) =>
38
- WORKFLOW_CATALOG !== name ? safeInvoke( handler, { eventId, id, runId, name }, 'onWorkflowStart' ) : null );
40
+ export const onWorkflowStart = handler => messageBus.on( BusEventType.WORKFLOW_START, ( { workflowDetails, ...eventFields } ) =>
41
+ shouldEmitWorkflowEvent( workflowDetails ) ? safeInvoke( handler, { workflowDetails, ...eventFields }, 'onWorkflowStart' ) : null );
39
42
 
40
43
  /** Listen to workflow end events, excludes catalog workflow */
41
- export const onWorkflowEnd = handler => messageBus.on( BusEventType.WORKFLOW_END, ( { eventId, id, runId, name, duration } ) =>
42
- WORKFLOW_CATALOG !== name ? safeInvoke( handler, { eventId, id, runId, name, duration }, 'onWorkflowEnd' ) : null );
44
+ export const onWorkflowEnd = handler => messageBus.on( BusEventType.WORKFLOW_END, ( { workflowDetails, ...eventFields } ) =>
45
+ shouldEmitWorkflowEvent( workflowDetails ) ? safeInvoke( handler, { workflowDetails, ...eventFields }, 'onWorkflowEnd' ) : null );
43
46
 
44
47
  /** Listen to workflow error events, excludes catalog workflow */
45
- export const onWorkflowError = handler => messageBus.on( BusEventType.WORKFLOW_ERROR, ( { eventId, id, runId, name, duration, error } ) =>
46
- WORKFLOW_CATALOG !== name ? safeInvoke( handler, { eventId, id, runId, name, duration, error }, 'onWorkflowError' ) : null );
48
+ export const onWorkflowError = handler => messageBus.on( BusEventType.WORKFLOW_ERROR, ( { workflowDetails, ...eventFields } ) =>
49
+ shouldEmitWorkflowEvent( workflowDetails ) ? safeInvoke( handler, { workflowDetails, ...eventFields }, 'onWorkflowError' ) : null );
47
50
 
48
51
  /** Generic listener for events emitted elsewhere (outside core) */
49
52
  export const on = ( eventName, handler ) => messageBus.on( `external:${eventName}`, payload =>