@outputai/core 0.8.2-next.57bc8d6.0 → 0.8.2-next.89b6f8c.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outputai/core",
3
- "version": "0.8.2-next.57bc8d6.0",
3
+ "version": "0.8.2-next.89b6f8c.0",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "exports": {
@@ -73,7 +73,6 @@
73
73
  "#internal_activities": "./src/internal_activities/index.js"
74
74
  },
75
75
  "scripts": {
76
- "worker": "node ./src/worker/index.js",
77
- "build": "pnpm exec tsc -p ./tsconfig.typecheck.json"
76
+ "worker": "node ./src/worker/index.js"
78
77
  }
79
78
  }
@@ -79,47 +79,6 @@ export interface WorkflowDetails {
79
79
  attempt: number;
80
80
  }
81
81
 
82
- /**
83
- * Attribute totals collected while an activity executes.
84
- */
85
- export interface Aggregations {
86
- /** Cost totals collected from HTTP request cost and LLM usage attributes. */
87
- cost: {
88
- total: number;
89
- };
90
- /** Token totals collected from LLM usage attributes. */
91
- tokens: {
92
- [tokenType: string]: number | undefined;
93
- total: number;
94
- };
95
- /** HTTP request count totals. */
96
- httpRequests: {
97
- total: number;
98
- };
99
- }
100
-
101
- /**
102
- * Common lifecycle hook payload fields for events associated with a workflow.
103
- */
104
- export interface HookPayloadBase {
105
- /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
106
- eventId: string;
107
- /** Timestamp of the event */
108
- eventDate: number;
109
- /** Information about the current workflow execution */
110
- workflowDetails: WorkflowDetails;
111
- }
112
-
113
- /**
114
- * Common hook payload fields for events associated with an activity.
115
- */
116
- export interface ActivityPayloadBase extends HookPayloadBase {
117
- /** Temporal's activityInfo(). */
118
- activityInfo: Info;
119
- /** Output component kind for the activity, e.g. step or evaluator. */
120
- outputActivityKind: string;
121
- }
122
-
123
82
  /**
124
83
  * Payload passed to the onError() handler when a workflow, activity or runtime error occurs.
125
84
  */
@@ -136,8 +95,6 @@ export interface ErrorHookPayload {
136
95
  activityInfo?: Info;
137
96
  /** Output component kind for the activity, e.g. step, evaluator, or internal_step. */
138
97
  outputActivityKind?: string;
139
- /** Attribute totals collected during the activity execution. */
140
- aggregations?: Aggregations | null;
141
98
  /** The error thrown. */
142
99
  error: Error;
143
100
  }
@@ -145,45 +102,37 @@ export interface ErrorHookPayload {
145
102
  /**
146
103
  * Payload passed to the onWorkflowStart() handler when a workflow run begins.
147
104
  */
148
- export type WorkflowStartHookPayload = HookPayloadBase;
149
-
150
- /**
151
- * Payload passed to the onWorkflowEnd() handler when a workflow run completes successfully.
152
- */
153
- export type WorkflowEndHookPayload = HookPayloadBase;
154
-
155
- /**
156
- * Payload passed to the onWorkflowError() handler when a workflow run fails.
157
- */
158
- export interface WorkflowErrorHookPayload extends HookPayloadBase {
159
- /** The error thrown. */
160
- error: Error;
105
+ export interface WorkflowStartHookPayload {
106
+ /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
107
+ eventId: string;
108
+ /** Timestamp of the event */
109
+ eventDate: number;
110
+ /** Information about the current workflow execution */
111
+ workflowDetails: WorkflowDetails;
161
112
  }
162
113
 
163
114
  /**
164
- * Common activity lifecycle hook payload fields.
165
- */
166
- export type ActivityHookPayload = ActivityPayloadBase;
167
-
168
- /**
169
- * Payload passed to the onActivityStart() handler when an activity starts.
170
- */
171
- export type ActivityStartHookPayload = ActivityHookPayload;
172
-
173
- /**
174
- * Payload passed to the onActivityEnd() handler when an activity completes successfully.
115
+ * Payload passed to the onWorkflowEnd() handler when a workflow run completes successfully.
175
116
  */
176
- export interface ActivityEndHookPayload extends ActivityHookPayload {
177
- /** Attribute totals collected during the activity execution. */
178
- aggregations: Aggregations | null;
117
+ export interface WorkflowEndHookPayload {
118
+ /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
119
+ eventId: string;
120
+ /** Timestamp of the event */
121
+ eventDate: number;
122
+ /** Information about the current workflow execution */
123
+ workflowDetails: WorkflowDetails;
179
124
  }
180
125
 
181
126
  /**
182
- * Payload passed to the onActivityError() handler when an activity fails.
127
+ * Payload passed to the onWorkflowError() handler when a workflow run fails.
183
128
  */
184
- export interface ActivityErrorHookPayload extends ActivityHookPayload {
185
- /** Attribute totals collected during the activity execution. */
186
- aggregations: Aggregations | null;
129
+ export interface WorkflowErrorHookPayload {
130
+ /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
131
+ eventId: string;
132
+ /** Timestamp of the event */
133
+ eventDate: number;
134
+ /** Information about the current workflow execution */
135
+ workflowDetails: WorkflowDetails;
187
136
  /** The error thrown. */
188
137
  error: Error;
189
138
  }
@@ -230,37 +179,16 @@ export declare function onWorkflowEnd( handler: ( payload: WorkflowEndHookPayloa
230
179
  */
231
180
  export declare function onWorkflowError( handler: ( payload: WorkflowErrorHookPayload ) => void ): void;
232
181
 
233
- /**
234
- * Register a handler to be invoked when an activity starts.
235
- *
236
- * Excludes internal activities.
237
- *
238
- * @param handler - Function called with the activity start payload.
239
- */
240
- export declare function onActivityStart( handler: ( payload: ActivityStartHookPayload ) => void ): void;
241
-
242
- /**
243
- * Register a handler to be invoked when an activity completes successfully.
244
- *
245
- * Excludes internal activities.
246
- *
247
- * @param handler - Function called with the activity end payload.
248
- */
249
- export declare function onActivityEnd( handler: ( payload: ActivityEndHookPayload ) => void ): void;
250
-
251
- /**
252
- * Register a handler to be invoked when an activity fails.
253
- *
254
- * Excludes internal activities.
255
- *
256
- * @param handler - Function called with the activity error payload.
257
- */
258
- export declare function onActivityError( handler: ( payload: ActivityErrorHookPayload ) => void ): void;
259
-
260
182
  /**
261
183
  * Framework-managed envelope added to payloads passed to on() handlers.
262
184
  */
263
- export interface OnHookEnvelope extends HookPayloadBase {
185
+ export interface OnHookEnvelope {
186
+ /** UUID v4 stamped per emit. Stable per-emit idempotency key. */
187
+ eventId: string;
188
+ /** Timestamp of the event */
189
+ eventDate: number;
190
+ /** Information about the current workflow execution */
191
+ workflowDetails: WorkflowDetails;
264
192
  /** Temporal's activityInfo(). */
265
193
  activityInfo: Info;
266
194
  /** Output component kind for the activity, e.g. step, evaluator, or internal_step. */
@@ -1,5 +1,5 @@
1
1
  import { messageBus } from '#bus';
2
- import { BusEventType, ComponentType, WORKFLOW_CATALOG } from '#consts';
2
+ import { BusEventType, WORKFLOW_CATALOG } from '#consts';
3
3
  import { createChildLogger } from '#logger';
4
4
 
5
5
  const log = createChildLogger( 'Hooks' );
@@ -48,21 +48,6 @@ export const onWorkflowEnd = handler => messageBus.on( BusEventType.WORKFLOW_END
48
48
  export const onWorkflowError = handler => messageBus.on( BusEventType.WORKFLOW_ERROR, ( { workflowDetails, ...eventFields } ) =>
49
49
  shouldEmitWorkflowEvent( workflowDetails ) ? safeInvoke( handler, { workflowDetails, ...eventFields }, 'onWorkflowError' ) : null );
50
50
 
51
- /** Internal activities do not trigger hooks */
52
- const shouldEmitActivityEvent = outputActivityKind => outputActivityKind !== ComponentType.INTERNAL_STEP;
53
-
54
- /** Listen to workflow start events, excludes catalog workflow */
55
- export const onActivityStart = handler => messageBus.on( BusEventType.ACTIVITY_START, ( { outputActivityKind, ...eventFields } ) =>
56
- shouldEmitActivityEvent( outputActivityKind ) ? safeInvoke( handler, { outputActivityKind, ...eventFields }, 'onActivityStart' ) : null );
57
-
58
- /** Listen to workflow end events, excludes catalog workflow */
59
- export const onActivityEnd = handler => messageBus.on( BusEventType.ACTIVITY_END, ( { outputActivityKind, ...eventFields } ) =>
60
- shouldEmitActivityEvent( outputActivityKind ) ? safeInvoke( handler, { outputActivityKind, ...eventFields }, 'onActivityEnd' ) : null );
61
-
62
- /** Listen to workflow error events, excludes catalog workflow */
63
- export const onActivityError = handler => messageBus.on( BusEventType.ACTIVITY_ERROR, ( { outputActivityKind, ...eventFields } ) =>
64
- shouldEmitActivityEvent( outputActivityKind ) ? safeInvoke( handler, { outputActivityKind, ...eventFields }, 'onActivityError' ) : null );
65
-
66
51
  /** Generic listener for events emitted elsewhere (outside core) */
67
52
  export const on = ( eventName, handler ) => messageBus.on( `external:${eventName}`, payload =>
68
53
  safeInvoke( handler, payload, eventName ) );
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { BusEventType, ComponentType, WORKFLOW_CATALOG } from '#consts';
2
+ import { BusEventType, WORKFLOW_CATALOG } from '#consts';
3
3
 
4
4
  const logErrorMock = vi.hoisted( () => vi.fn() );
5
5
  const createChildLoggerMock = vi.hoisted( () =>
@@ -18,9 +18,6 @@ vi.mock( '#bus', () => ( { messageBus: messageBusMock } ) );
18
18
 
19
19
  import {
20
20
  on,
21
- onActivityEnd,
22
- onActivityError,
23
- onActivityStart,
24
21
  onBeforeWorkerStart,
25
22
  onError,
26
23
  onWorkflowEnd,
@@ -52,12 +49,6 @@ const activityInfo = {
52
49
 
53
50
  const eventDate = 1710000001234;
54
51
 
55
- const aggregations = {
56
- cost: { total: 0 },
57
- tokens: { total: 0 },
58
- httpRequests: { total: 1 }
59
- };
60
-
61
52
  describe( 'hooks/index', () => {
62
53
  beforeEach( () => {
63
54
  vi.clearAllMocks();
@@ -207,51 +198,6 @@ describe( 'hooks/index', () => {
207
198
  } );
208
199
  } );
209
200
 
210
- describe( 'activity lifecycle hooks', () => {
211
- const cases = [
212
- [ 'onActivityStart', onActivityStart, BusEventType.ACTIVITY_START, undefined ],
213
- [ 'onActivityEnd', onActivityEnd, BusEventType.ACTIVITY_END, { aggregations } ],
214
- [ 'onActivityError', onActivityError, BusEventType.ACTIVITY_ERROR, { error: new Error( 'activity failed' ) } ]
215
- ];
216
-
217
- it.each( cases )( '%s skips internal activities and forwards bus fields', async ( _name, registerHook, eventType, extraFields = {} ) => {
218
- const handler = vi.fn().mockResolvedValue( undefined );
219
- registerHook( handler );
220
-
221
- expect( messageBusMock.on ).toHaveBeenCalledWith( eventType, expect.any( Function ) );
222
-
223
- await Promise.resolve( onHandlers[eventType]( {
224
- eventId: 'evt-ignored',
225
- eventDate,
226
- activityInfo,
227
- workflowDetails,
228
- outputActivityKind: ComponentType.INTERNAL_STEP,
229
- ...extraFields
230
- } ) );
231
- expect( handler ).not.toHaveBeenCalled();
232
-
233
- await Promise.resolve( onHandlers[eventType]( {
234
- eventId: 'evt-activity-1',
235
- eventDate,
236
- activityInfo,
237
- workflowDetails,
238
- outputActivityKind: ComponentType.STEP,
239
- extra: 'passthrough',
240
- ...extraFields
241
- } ) );
242
-
243
- expect( handler ).toHaveBeenCalledWith( {
244
- eventId: 'evt-activity-1',
245
- eventDate,
246
- activityInfo,
247
- workflowDetails,
248
- outputActivityKind: ComponentType.STEP,
249
- extra: 'passthrough',
250
- ...extraFields
251
- } );
252
- } );
253
- } );
254
-
255
201
  describe( 'on', () => {
256
202
  it( 'subscribes to external event channel and forwards payload', async () => {
257
203
  const handler = vi.fn().mockResolvedValue( undefined );
package/src/index.d.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  * Export all types from the interface
3
3
  *
4
4
  */
5
- export * from './interface/index.js';
5
+ export * from './interface/index.d.ts';
6
6
 
7
7
  /**
8
8
  * Exports all errors
9
9
  */
10
- export * from './errors.js';
10
+ export * from './errors.d.ts';
11
11
 
12
12
  /**
13
13
  * Expose z from Zod as a convenience.
@@ -22,7 +22,7 @@ export type EvaluatorOptions = {
22
22
  */
23
23
  export type EvaluatorFunction<
24
24
  InputSchema extends AnyZodSchema | undefined = undefined,
25
- Result extends EvaluationResult = EvaluationResult
25
+ Result extends EvaluationResult
26
26
  > = InputSchema extends AnyZodSchema ?
27
27
  ( input: z.infer<InputSchema> ) => Promise<Result> :
28
28
  () => Promise<Result>;
@@ -34,7 +34,7 @@ export type EvaluatorFunction<
34
34
  *
35
35
  * It adds input validation based on the `inputSchema`.
36
36
  */
37
- export type EvaluatorFunctionWrapper<EvaluatorFunction extends ( ...args: any ) => any> = // eslint-disable-line @typescript-eslint/no-explicit-any
37
+ export type EvaluatorFunctionWrapper<EvaluatorFunction> =
38
38
  Parameters<EvaluatorFunction> extends [infer Input] ?
39
39
  ( input: Input ) => ReturnType<EvaluatorFunction> :
40
40
  () => ReturnType<EvaluatorFunction>;
@@ -36,7 +36,7 @@ export type StepFunction<
36
36
  * @param input - The Step input; it matches the schema defined by `inputSchema`.
37
37
  * @returns A value matching the schema defined by `outputSchema`.
38
38
  */
39
- export type StepFunctionWrapper<StepFunction extends ( ...args: any ) => any> = // eslint-disable-line @typescript-eslint/no-explicit-any
39
+ export type StepFunctionWrapper<StepFunction> =
40
40
  Parameters<StepFunction> extends [infer Input] ?
41
41
  ( input: Input ) => ReturnType<StepFunction> :
42
42
  () => ReturnType<StepFunction>;
@@ -133,7 +133,7 @@ export type WorkflowFunction<
133
133
  > = InputSchema extends AnyZodSchema ?
134
134
  ( input: z.infer<InputSchema>, context: WorkflowContext<InputSchema, OutputSchema> ) =>
135
135
  Promise<OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void> :
136
- ( input: undefined | null, context: WorkflowContext<InputSchema, OutputSchema> ) =>
136
+ ( input?: undefined | null, context: WorkflowContext<InputSchema, OutputSchema> ) =>
137
137
  Promise<OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void>;
138
138
 
139
139
  /**
@@ -149,7 +149,7 @@ export type WorkflowFunction<
149
149
  * @param options - Additional options for the invocation.
150
150
  * @returns A value matching the schema defined by `outputSchema`.
151
151
  */
152
- export type WorkflowFunctionWrapper<WorkflowFunction extends ( ...args: any ) => any> = // eslint-disable-line @typescript-eslint/no-explicit-any
152
+ export type WorkflowFunctionWrapper<WorkflowFunction> =
153
153
  [Parameters<WorkflowFunction>[0]] extends [undefined | null] ?
154
154
  ( input?: undefined | null, options?: WorkflowInvocationOptions ) =>
155
155
  ReturnType<WorkflowFunction> :
@@ -77,18 +77,21 @@ export class ActivityExecutionInterceptor {
77
77
 
78
78
  const output = await Storage.runWithContext( async _ => next( input ), storageContext );
79
79
 
80
- const aggregations = state.attributes.length > 0 ? aggregateAttributes( state.attributes ) : null;
81
-
82
- messageBus.emit( BusEventType.ACTIVITY_END, { activityInfo, aggregations, workflowDetails, outputActivityKind } );
80
+ messageBus.emit( BusEventType.ACTIVITY_END, { activityInfo, workflowDetails, outputActivityKind } );
83
81
  Tracing.addEventEnd( { id: activityId, details: output, traceInfo } );
84
82
 
85
- return { [ACTIVITY_WRAPPER_VERSION_FIELD]: 1, output, aggregations };
83
+ return {
84
+ [ACTIVITY_WRAPPER_VERSION_FIELD]: 1,
85
+ output,
86
+ aggregations: state.attributes.length > 0 ? aggregateAttributes( state.attributes ) : null
87
+ };
86
88
 
87
89
  } catch ( error ) {
88
- const aggregations = state.attributes.length > 0 ? aggregateAttributes( state.attributes ) : null;
89
- messageBus.emit( BusEventType.ACTIVITY_ERROR, { activityInfo, aggregations, workflowDetails, outputActivityKind, error } );
90
+ messageBus.emit( BusEventType.ACTIVITY_ERROR, { activityInfo, workflowDetails, outputActivityKind, error } );
90
91
  Tracing.addEventError( { id: activityId, details: error, traceInfo } );
91
92
 
93
+ const aggregations = state.attributes.length > 0 ? aggregateAttributes( state.attributes ) : null;
94
+
92
95
  throw aggregations ? buildApplicationFailureWithDetails( error, { aggregations } ) : error;
93
96
  } finally {
94
97
  clearInterval( state.heartbeat );
@@ -100,12 +100,6 @@ const httpRequestAttribute = {
100
100
  requestId: 'req-1'
101
101
  };
102
102
 
103
- const httpRequestAggregations = {
104
- cost: { total: 0 },
105
- tokens: { total: 0 },
106
- httpRequests: { total: 1 }
107
- };
108
-
109
103
  describe( 'ActivityExecutionInterceptor', () => {
110
104
  beforeEach( () => {
111
105
  vi.clearAllMocks();
@@ -142,7 +136,7 @@ describe( 'ActivityExecutionInterceptor', () => {
142
136
  );
143
137
  expect( messageBusEmitMock ).toHaveBeenCalledWith(
144
138
  BusEventType.ACTIVITY_END,
145
- { activityInfo: activityInfoMock, aggregations: null, workflowDetails: workflowDetailsMock, outputActivityKind: 'step' }
139
+ { activityInfo: activityInfoMock, workflowDetails: workflowDetailsMock, outputActivityKind: 'step' }
146
140
  );
147
141
  expect( addEventStartMock ).toHaveBeenCalledWith( {
148
142
  id: 'act-1',
@@ -195,16 +189,14 @@ describe( 'ActivityExecutionInterceptor', () => {
195
189
 
196
190
  await expect( interceptor.execute( makeInput(), next ) ).resolves.toEqual( {
197
191
  output: { result: 'ok' },
198
- aggregations: httpRequestAggregations,
192
+ aggregations: {
193
+ cost: { total: 0 },
194
+ tokens: { total: 0 },
195
+ httpRequests: { total: 1 }
196
+ },
199
197
  [ACTIVITY_WRAPPER_VERSION_FIELD]: 1
200
198
  } );
201
199
 
202
- expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_END, {
203
- activityInfo: activityInfoMock,
204
- aggregations: httpRequestAggregations,
205
- workflowDetails: workflowDetailsMock,
206
- outputActivityKind: 'step'
207
- } );
208
200
  } );
209
201
 
210
202
  it( 'stores collected aggregations in ApplicationFailure details after failed execution', async () => {
@@ -223,17 +215,15 @@ describe( 'ActivityExecutionInterceptor', () => {
223
215
  message: 'step failed',
224
216
  type: 'Error',
225
217
  details: [ {
226
- aggregations: httpRequestAggregations
218
+ aggregations: {
219
+ cost: { total: 0 },
220
+ tokens: { total: 0 },
221
+ httpRequests: { total: 1 }
222
+ }
227
223
  } ],
228
224
  cause: error
229
225
  } );
230
- expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, {
231
- activityInfo: activityInfoMock,
232
- aggregations: httpRequestAggregations,
233
- workflowDetails: workflowDetailsMock,
234
- outputActivityKind: 'step',
235
- error
236
- } );
226
+ expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, expect.objectContaining( { error } ) );
237
227
  expect( addEventErrorMock ).toHaveBeenCalledOnce();
238
228
  expect( addEventEndMock ).not.toHaveBeenCalled();
239
229
  } );
@@ -253,7 +243,13 @@ describe( 'ActivityExecutionInterceptor', () => {
253
243
 
254
244
  expect( thrown.details ).toEqual( [
255
245
  { domain: { reason: 'bad-input' } },
256
- { aggregations: httpRequestAggregations }
246
+ {
247
+ aggregations: {
248
+ cost: { total: 0 },
249
+ tokens: { total: 0 },
250
+ httpRequests: { total: 1 }
251
+ }
252
+ }
257
253
  ] );
258
254
  } );
259
255
 
@@ -304,7 +300,13 @@ describe( 'ActivityExecutionInterceptor', () => {
304
300
  nonRetryable: true,
305
301
  details: [
306
302
  { domain: { reason: 'bad-input' } },
307
- { aggregations: httpRequestAggregations }
303
+ {
304
+ aggregations: {
305
+ cost: { total: 0 },
306
+ tokens: { total: 0 },
307
+ httpRequests: { total: 1 }
308
+ }
309
+ }
308
310
  ]
309
311
  } );
310
312
  } );
@@ -322,7 +324,6 @@ describe( 'ActivityExecutionInterceptor', () => {
322
324
  expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_START, expect.any( Object ) );
323
325
  expect( messageBusEmitMock ).toHaveBeenCalledWith( BusEventType.ACTIVITY_ERROR, {
324
326
  activityInfo: activityInfoMock,
325
- aggregations: null,
326
327
  workflowDetails: workflowDetailsMock,
327
328
  outputActivityKind: 'step',
328
329
  error