@codemation/core 0.7.0 → 0.8.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 (79) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{EngineRuntimeRegistration.types-_M7KFD3D.d.ts → EngineRuntimeRegistration.types-BP6tsaNP.d.ts} +4 -2
  3. package/dist/{EngineWorkflowRunnerService-D0Cwngv7.d.cts → EngineWorkflowRunnerService-DzOCa1BW.d.cts} +4 -2
  4. package/dist/{InMemoryRunDataFactory-BIWx6e02.d.cts → InMemoryRunDataFactory-1iz7_SnO.d.cts} +24 -4
  5. package/dist/{workflowActivationPolicy-6V3OJD3N.cjs → InMemoryRunEventBusRegistry-B0_C4OnP.cjs} +1 -16
  6. package/dist/InMemoryRunEventBusRegistry-B0_C4OnP.cjs.map +1 -0
  7. package/dist/{workflowActivationPolicy-Td9HTOuD.js → InMemoryRunEventBusRegistry-C2U83Hmv.js} +2 -11
  8. package/dist/InMemoryRunEventBusRegistry-C2U83Hmv.js.map +1 -0
  9. package/dist/{RunIntentService-CuXAIO6_.d.ts → RunIntentService-BqhmdoA1.d.ts} +231 -3
  10. package/dist/{RunIntentService-5k0p-J67.d.cts → RunIntentService-S-1lW-gS.d.cts} +203 -3
  11. package/dist/bootstrap/index.cjs +4 -2
  12. package/dist/bootstrap/index.d.cts +24 -5
  13. package/dist/bootstrap/index.d.ts +4 -4
  14. package/dist/bootstrap/index.js +3 -3
  15. package/dist/{bootstrap-D-TDU9Lu.cjs → bootstrap-BaN6hZ5I.cjs} +6 -3
  16. package/dist/bootstrap-BaN6hZ5I.cjs.map +1 -0
  17. package/dist/{bootstrap-BhYxSivA.js → bootstrap-d_BMaDT4.js} +6 -3
  18. package/dist/bootstrap-d_BMaDT4.js.map +1 -0
  19. package/dist/{index-BnJ7_IrO.d.ts → index-CVs9rVhl.d.ts} +36 -6
  20. package/dist/index.cjs +48 -83
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +23 -4
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +27 -74
  25. package/dist/index.js.map +1 -1
  26. package/dist/{runtime-CJnObwsU.js → runtime-DUW6tIJ1.js} +423 -33
  27. package/dist/runtime-DUW6tIJ1.js.map +1 -0
  28. package/dist/{runtime-3YVDd2vY.cjs → runtime-Dvo2ru5A.cjs} +548 -32
  29. package/dist/runtime-Dvo2ru5A.cjs.map +1 -0
  30. package/dist/testing.cjs +6 -6
  31. package/dist/testing.cjs.map +1 -1
  32. package/dist/testing.d.cts +2 -2
  33. package/dist/testing.d.ts +2 -2
  34. package/dist/testing.js +3 -3
  35. package/package.json +1 -1
  36. package/src/ai/AiHost.ts +2 -0
  37. package/src/ai/CallableToolConfig.ts +28 -3
  38. package/src/bootstrap/index.ts +6 -1
  39. package/src/bootstrap/runtime/EngineRuntimeRegistrar.ts +1 -0
  40. package/src/bootstrap/runtime/EngineRuntimeRegistration.types.ts +3 -0
  41. package/src/contracts/CodemationTelemetryAttributeNames.ts +12 -0
  42. package/src/contracts/CodemationTelemetryMetricNames.ts +7 -0
  43. package/src/contracts/CostCatalogContract.ts +16 -0
  44. package/src/contracts/CostTrackingTelemetryContract.ts +47 -0
  45. package/src/contracts/GenAiTelemetryAttributeNames.ts +10 -0
  46. package/src/contracts/NoOpCostTrackingTelemetry.ts +16 -0
  47. package/src/contracts/NoOpCostTrackingTelemetryFactory.ts +9 -0
  48. package/src/contracts/NoOpExecutionTelemetry.ts +26 -0
  49. package/src/contracts/NoOpExecutionTelemetryFactory.ts +16 -0
  50. package/src/contracts/NoOpNodeExecutionTelemetry.ts +15 -0
  51. package/src/contracts/NoOpTelemetryArtifactReference.ts +9 -0
  52. package/src/contracts/NoOpTelemetrySpanScope.ts +22 -0
  53. package/src/contracts/index.ts +1 -0
  54. package/src/contracts/runTypes.ts +4 -1
  55. package/src/contracts/runtimeTypes.ts +7 -0
  56. package/src/contracts/telemetryTypes.ts +105 -0
  57. package/src/contracts/workflowTypes.ts +9 -0
  58. package/src/events/EventPublishingWorkflowExecutionRepository.ts +1 -1
  59. package/src/execution/CatalogBackedCostTrackingTelemetry.ts +81 -0
  60. package/src/execution/CatalogBackedCostTrackingTelemetryFactory.ts +12 -0
  61. package/src/execution/DefaultExecutionContextFactory.ts +23 -0
  62. package/src/execution/ExecutionTelemetryCostTrackingDecoratorFactory.ts +84 -0
  63. package/src/execution/NodeActivationRequestComposer.ts +1 -0
  64. package/src/execution/NodeExecutionSnapshotFactory.ts +2 -0
  65. package/src/execution/StaticCostCatalog.ts +22 -0
  66. package/src/execution/WorkflowRunExecutionContextFactory.ts +2 -0
  67. package/src/execution/index.ts +4 -0
  68. package/src/orchestration/NodeExecutionRequestHandlerService.ts +1 -0
  69. package/src/orchestration/RunContinuationService.ts +4 -0
  70. package/src/orchestration/RunStartService.ts +2 -0
  71. package/src/policies/storage/RunPolicySnapshotFactory.ts +9 -0
  72. package/src/runStorage/InMemoryWorkflowExecutionRepository.ts +4 -2
  73. package/src/types/index.ts +5 -0
  74. package/dist/bootstrap-BhYxSivA.js.map +0 -1
  75. package/dist/bootstrap-D-TDU9Lu.cjs.map +0 -1
  76. package/dist/runtime-3YVDd2vY.cjs.map +0 -1
  77. package/dist/runtime-CJnObwsU.js.map +0 -1
  78. package/dist/workflowActivationPolicy-6V3OJD3N.cjs.map +0 -1
  79. package/dist/workflowActivationPolicy-Td9HTOuD.js.map +0 -1
@@ -2,6 +2,7 @@ import type { ReadableStream as BinaryReadableStream } from "node:stream/web";
2
2
  import type { TypeToken } from "../di";
3
3
  import type { RunEventBus } from "../events/runEvents";
4
4
  import type { CredentialSessionService } from "./credentialTypes";
5
+ import type { ExecutionTelemetry, ExecutionTelemetryFactory, NodeExecutionTelemetry } from "./telemetryTypes";
5
6
  import type {
6
7
  ConnectionInvocationAppendArgs,
7
8
  NodeInputsByPort,
@@ -28,6 +29,7 @@ import type {
28
29
  RunnableNodeConfig,
29
30
  OutputPortKey,
30
31
  ParentExecutionRef,
32
+ PersistedRunPolicySnapshot,
31
33
  RunDataFactory,
32
34
  RunDataSnapshot,
33
35
  RunId,
@@ -149,6 +151,7 @@ export interface ExecutionContext {
149
151
  now: () => Date;
150
152
  data: RunDataSnapshot;
151
153
  nodeState?: NodeExecutionStatePublisher;
154
+ telemetry: ExecutionTelemetry;
152
155
  binary: ExecutionBinaryService;
153
156
  getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
154
157
  }
@@ -158,11 +161,13 @@ export interface ExecutionContextFactory {
158
161
  runId: RunId;
159
162
  workflowId: WorkflowId;
160
163
  parent?: ParentExecutionRef;
164
+ policySnapshot?: PersistedRunPolicySnapshot;
161
165
  subworkflowDepth: number;
162
166
  engineMaxNodeActivations: number;
163
167
  engineMaxSubworkflowDepth: number;
164
168
  data: RunDataSnapshot;
165
169
  nodeState?: NodeExecutionStatePublisher;
170
+ telemetry?: ExecutionTelemetry;
166
171
  getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
167
172
  }): ExecutionContext;
168
173
  }
@@ -171,6 +176,7 @@ export interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfi
171
176
  nodeId: NodeId;
172
177
  activationId: NodeActivationId;
173
178
  config: TConfig;
179
+ telemetry: NodeExecutionTelemetry;
174
180
  binary: NodeBinaryAttachmentService;
175
181
  }
176
182
 
@@ -416,6 +422,7 @@ export interface EngineDeps {
416
422
  activationScheduler: NodeActivationScheduler;
417
423
  runDataFactory: RunDataFactory;
418
424
  executionContextFactory: ExecutionContextFactory;
425
+ executionTelemetryFactory?: ExecutionTelemetryFactory;
419
426
  nodeExecutor: NodeExecutor;
420
427
  eventBus?: RunEventBus;
421
428
  tokenRegistry: PersistedWorkflowTokenRegistryLike;
@@ -0,0 +1,105 @@
1
+ import type {
2
+ JsonValue,
3
+ NodeActivationId,
4
+ NodeId,
5
+ ParentExecutionRef,
6
+ PersistedRunPolicySnapshot,
7
+ RunId,
8
+ WorkflowId,
9
+ } from "./workflowTypes";
10
+ import type { CostTrackingTelemetry } from "./CostTrackingTelemetryContract";
11
+
12
+ export type TelemetryAttributePrimitive = string | number | boolean | null;
13
+
14
+ export interface TelemetryAttributes {
15
+ readonly [key: string]: TelemetryAttributePrimitive | undefined;
16
+ }
17
+
18
+ export interface TelemetryMetricRecord {
19
+ readonly name: string;
20
+ readonly value: number;
21
+ readonly unit?: string;
22
+ readonly attributes?: TelemetryAttributes;
23
+ }
24
+
25
+ export interface TelemetrySpanEventRecord {
26
+ readonly name: string;
27
+ readonly occurredAt?: Date;
28
+ readonly attributes?: TelemetryAttributes;
29
+ }
30
+
31
+ export interface TelemetryArtifactAttachment {
32
+ readonly kind: string;
33
+ readonly contentType: string;
34
+ readonly previewText?: string;
35
+ readonly previewJson?: JsonValue;
36
+ readonly payloadText?: string;
37
+ readonly payloadJson?: JsonValue;
38
+ readonly bytes?: number;
39
+ readonly truncated?: boolean;
40
+ readonly expiresAt?: Date;
41
+ }
42
+
43
+ export interface TelemetryArtifactReference {
44
+ readonly artifactId: string;
45
+ readonly traceId?: string;
46
+ readonly spanId?: string;
47
+ }
48
+
49
+ export interface TelemetrySpanEnd {
50
+ readonly status?: "ok" | "error";
51
+ readonly statusMessage?: string;
52
+ readonly endedAt?: Date;
53
+ readonly attributes?: TelemetryAttributes;
54
+ }
55
+
56
+ export interface TelemetryChildSpanStart {
57
+ readonly name: string;
58
+ readonly kind?: "internal" | "client";
59
+ readonly startedAt?: Date;
60
+ readonly attributes?: TelemetryAttributes;
61
+ }
62
+
63
+ export interface TelemetryScope {
64
+ readonly traceId?: string;
65
+ readonly spanId?: string;
66
+ readonly costTracking?: CostTrackingTelemetry;
67
+ addSpanEvent(args: TelemetrySpanEventRecord): Promise<void> | void;
68
+ recordMetric(args: TelemetryMetricRecord): Promise<void> | void;
69
+ attachArtifact(args: TelemetryArtifactAttachment): Promise<TelemetryArtifactReference> | TelemetryArtifactReference;
70
+ }
71
+
72
+ export interface TelemetrySpanScope extends TelemetryScope {
73
+ readonly traceId: string;
74
+ readonly spanId: string;
75
+ end(args?: TelemetrySpanEnd): Promise<void> | void;
76
+ }
77
+
78
+ export interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
79
+ startChildSpan(args: TelemetryChildSpanStart): TelemetrySpanScope;
80
+ }
81
+
82
+ export interface ExecutionTelemetry extends TelemetryScope {
83
+ readonly traceId: string;
84
+ readonly spanId: string;
85
+ forNode(args: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry;
86
+ }
87
+
88
+ export interface ExecutionTelemetryFactory {
89
+ create(
90
+ args: Readonly<{
91
+ runId: RunId;
92
+ workflowId: WorkflowId;
93
+ parent?: ParentExecutionRef;
94
+ policySnapshot?: PersistedRunPolicySnapshot;
95
+ }>,
96
+ ): ExecutionTelemetry;
97
+ }
98
+ export { NoOpTelemetryArtifactReference } from "./NoOpTelemetryArtifactReference";
99
+ export { NoOpTelemetrySpanScope } from "./NoOpTelemetrySpanScope";
100
+ export { NoOpNodeExecutionTelemetry } from "./NoOpNodeExecutionTelemetry";
101
+ export { NoOpExecutionTelemetry } from "./NoOpExecutionTelemetry";
102
+ export { NoOpExecutionTelemetryFactory } from "./NoOpExecutionTelemetryFactory";
103
+ export { CodemationTelemetryAttributeNames } from "./CodemationTelemetryAttributeNames";
104
+ export { GenAiTelemetryAttributeNames } from "./GenAiTelemetryAttributeNames";
105
+ export { CodemationTelemetryMetricNames } from "./CodemationTelemetryMetricNames";
@@ -269,11 +269,17 @@ export interface WorkflowStoragePolicyDecisionArgs {
269
269
  export interface WorkflowPrunePolicySpec {
270
270
  readonly runDataRetentionSeconds?: number;
271
271
  readonly binaryRetentionSeconds?: number;
272
+ readonly telemetrySpanRetentionSeconds?: number;
273
+ readonly telemetryArtifactRetentionSeconds?: number;
274
+ readonly telemetryMetricRetentionSeconds?: number;
272
275
  }
273
276
 
274
277
  export interface PersistedRunPolicySnapshot {
275
278
  readonly retentionSeconds?: number;
276
279
  readonly binaryRetentionSeconds?: number;
280
+ readonly telemetrySpanRetentionSeconds?: number;
281
+ readonly telemetryArtifactRetentionSeconds?: number;
282
+ readonly telemetryMetricRetentionSeconds?: number;
277
283
  readonly storagePolicy: WorkflowStoragePolicyMode;
278
284
  }
279
285
 
@@ -311,5 +317,8 @@ export type NodeErrorHandlerSpec = TypeToken<NodeErrorHandler> | NodeErrorHandle
311
317
  export interface WorkflowPolicyRuntimeDefaults {
312
318
  readonly retentionSeconds?: number;
313
319
  readonly binaryRetentionSeconds?: number;
320
+ readonly telemetrySpanRetentionSeconds?: number;
321
+ readonly telemetryArtifactRetentionSeconds?: number;
322
+ readonly telemetryMetricRetentionSeconds?: number;
314
323
  readonly storagePolicy?: WorkflowStoragePolicyMode;
315
324
  }
@@ -63,7 +63,7 @@ export class EventPublishingWorkflowExecutionRepository
63
63
  }
64
64
 
65
65
  async listRunsOlderThan(
66
- args: Readonly<{ beforeIso: string; limit?: number }>,
66
+ args: Readonly<{ nowIso: string; defaultRetentionSeconds: number; limit?: number }>,
67
67
  ): Promise<ReadonlyArray<RunPruneCandidate>> {
68
68
  const pruneRepository = this.inner as unknown as Partial<WorkflowExecutionPruneRepository>;
69
69
  if (!pruneRepository.listRunsOlderThan) return [];
@@ -0,0 +1,81 @@
1
+ import type { CostCatalog } from "../contracts/CostCatalogContract";
2
+ import {
3
+ CostTrackingTelemetryAttributeNames,
4
+ CostTrackingTelemetryMetricNames,
5
+ type CostTrackingPriceQuote,
6
+ type CostTrackingTelemetry,
7
+ type CostTrackingUsageRecord,
8
+ } from "../contracts/CostTrackingTelemetryContract";
9
+ import type { TelemetryAttributes, TelemetryScope } from "../types";
10
+
11
+ export class CatalogBackedCostTrackingTelemetry implements CostTrackingTelemetry {
12
+ constructor(
13
+ private readonly currentScope: TelemetryScope,
14
+ private readonly costCatalog: CostCatalog,
15
+ ) {}
16
+
17
+ async captureUsage(args: CostTrackingUsageRecord): Promise<CostTrackingPriceQuote | undefined> {
18
+ const usageAttributes = this.createUsageAttributes(args);
19
+ await this.currentScope.recordMetric({
20
+ name: CostTrackingTelemetryMetricNames.usage,
21
+ value: args.quantity,
22
+ unit: args.usageUnit,
23
+ attributes: usageAttributes,
24
+ });
25
+
26
+ const catalogEntry = this.costCatalog.findEntry(args);
27
+ if (!catalogEntry) {
28
+ return undefined;
29
+ }
30
+
31
+ const estimatedAmountMinor = Math.round(args.quantity * catalogEntry.pricePerUnitMinor);
32
+ const costAttributes = this.createCostAttributes(args, catalogEntry.currency, catalogEntry.currencyScale);
33
+ await this.currentScope.recordMetric({
34
+ name: CostTrackingTelemetryMetricNames.estimatedCost,
35
+ value: estimatedAmountMinor,
36
+ unit: catalogEntry.currency,
37
+ attributes: costAttributes,
38
+ });
39
+
40
+ return {
41
+ currency: catalogEntry.currency,
42
+ currencyScale: catalogEntry.currencyScale,
43
+ estimatedAmountMinor,
44
+ estimateKind: "catalog",
45
+ };
46
+ }
47
+
48
+ forScope(scope: TelemetryScope): CostTrackingTelemetry {
49
+ // eslint-disable-next-line codemation/no-manual-di-new
50
+ return new CatalogBackedCostTrackingTelemetry(scope, this.costCatalog);
51
+ }
52
+
53
+ private createUsageAttributes(args: CostTrackingUsageRecord): TelemetryAttributes {
54
+ return {
55
+ ...args.attributes,
56
+ [CostTrackingTelemetryAttributeNames.component]: args.component,
57
+ [CostTrackingTelemetryAttributeNames.provider]: args.provider,
58
+ [CostTrackingTelemetryAttributeNames.operation]: args.operation,
59
+ [CostTrackingTelemetryAttributeNames.pricingKey]: args.pricingKey,
60
+ [CostTrackingTelemetryAttributeNames.usageUnit]: args.usageUnit,
61
+ };
62
+ }
63
+
64
+ private createCostAttributes(
65
+ args: CostTrackingUsageRecord,
66
+ currency: string,
67
+ currencyScale: number,
68
+ ): TelemetryAttributes {
69
+ return {
70
+ ...args.attributes,
71
+ [CostTrackingTelemetryAttributeNames.component]: args.component,
72
+ [CostTrackingTelemetryAttributeNames.provider]: args.provider,
73
+ [CostTrackingTelemetryAttributeNames.operation]: args.operation,
74
+ [CostTrackingTelemetryAttributeNames.pricingKey]: args.pricingKey,
75
+ [CostTrackingTelemetryAttributeNames.usageUnit]: args.usageUnit,
76
+ [CostTrackingTelemetryAttributeNames.currency]: currency,
77
+ [CostTrackingTelemetryAttributeNames.currencyScale]: currencyScale,
78
+ [CostTrackingTelemetryAttributeNames.estimateKind]: "catalog",
79
+ };
80
+ }
81
+ }
@@ -0,0 +1,12 @@
1
+ import type { CostCatalog } from "../contracts/CostCatalogContract";
2
+ import type { CostTrackingTelemetry, CostTrackingTelemetryFactory } from "../contracts/CostTrackingTelemetryContract";
3
+ import type { ExecutionTelemetry } from "../contracts/telemetryTypes";
4
+ import { CatalogBackedCostTrackingTelemetry } from "./CatalogBackedCostTrackingTelemetry";
5
+
6
+ export class CatalogBackedCostTrackingTelemetryFactory implements CostTrackingTelemetryFactory {
7
+ constructor(private readonly costCatalog: CostCatalog) {}
8
+
9
+ create(args: Readonly<{ telemetry: ExecutionTelemetry }>): CostTrackingTelemetry {
10
+ return new CatalogBackedCostTrackingTelemetry(args.telemetry, this.costCatalog);
11
+ }
12
+ }
@@ -1,22 +1,30 @@
1
1
  import type {
2
2
  BinaryStorage,
3
+ CostTrackingTelemetryFactory,
3
4
  ExecutionContext,
4
5
  ExecutionContextFactory,
6
+ ExecutionTelemetryFactory,
5
7
  NodeExecutionStatePublisher,
6
8
  ParentExecutionRef,
7
9
  RunDataSnapshot,
8
10
  RunId,
9
11
  WorkflowId,
10
12
  } from "../types";
13
+ import { NoOpCostTrackingTelemetryFactory, NoOpExecutionTelemetryFactory } from "../types";
11
14
 
12
15
  import {
13
16
  DefaultExecutionBinaryService,
14
17
  UnavailableBinaryStorage,
15
18
  } from "../binaries/DefaultExecutionBinaryServiceFactory";
19
+ import { ExecutionTelemetryCostTrackingDecoratorFactory } from "./ExecutionTelemetryCostTrackingDecoratorFactory";
16
20
 
17
21
  export class DefaultExecutionContextFactory implements ExecutionContextFactory {
22
+ private readonly telemetryDecoratorFactory = new ExecutionTelemetryCostTrackingDecoratorFactory();
23
+
18
24
  constructor(
19
25
  private readonly binaryStorage: BinaryStorage = new UnavailableBinaryStorage(),
26
+ private readonly telemetryFactory: ExecutionTelemetryFactory = new NoOpExecutionTelemetryFactory(),
27
+ private readonly costTrackingFactory: CostTrackingTelemetryFactory = new NoOpCostTrackingTelemetryFactory(),
20
28
  private readonly currentDate: () => Date = () => new Date(),
21
29
  ) {}
22
30
 
@@ -24,13 +32,27 @@ export class DefaultExecutionContextFactory implements ExecutionContextFactory {
24
32
  runId: RunId;
25
33
  workflowId: WorkflowId;
26
34
  parent?: ParentExecutionRef;
35
+ policySnapshot?: import("../types").PersistedRunPolicySnapshot;
27
36
  subworkflowDepth: number;
28
37
  engineMaxNodeActivations: number;
29
38
  engineMaxSubworkflowDepth: number;
30
39
  data: RunDataSnapshot;
31
40
  nodeState?: NodeExecutionStatePublisher;
41
+ telemetry?: ExecutionContext["telemetry"];
32
42
  getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
33
43
  }): ExecutionContext {
44
+ const baseTelemetry =
45
+ args.telemetry ??
46
+ this.telemetryFactory.create({
47
+ runId: args.runId,
48
+ workflowId: args.workflowId,
49
+ parent: args.parent,
50
+ policySnapshot: args.policySnapshot,
51
+ });
52
+ const telemetry = this.telemetryDecoratorFactory.decorateExecutionTelemetry({
53
+ telemetry: baseTelemetry,
54
+ costTracking: baseTelemetry.costTracking ?? this.costTrackingFactory.create({ telemetry: baseTelemetry }),
55
+ });
34
56
  return {
35
57
  runId: args.runId,
36
58
  workflowId: args.workflowId,
@@ -41,6 +63,7 @@ export class DefaultExecutionContextFactory implements ExecutionContextFactory {
41
63
  now: this.currentDate,
42
64
  data: args.data,
43
65
  nodeState: args.nodeState,
66
+ telemetry,
44
67
  binary: new DefaultExecutionBinaryService(this.binaryStorage, args.workflowId, args.runId, this.currentDate),
45
68
  getCredential: args.getCredential,
46
69
  };
@@ -0,0 +1,84 @@
1
+ import type { CostTrackingTelemetry } from "../contracts/CostTrackingTelemetryContract";
2
+ import type {
3
+ ExecutionTelemetry,
4
+ NodeExecutionTelemetry,
5
+ TelemetryArtifactAttachment,
6
+ TelemetryArtifactReference,
7
+ TelemetryChildSpanStart,
8
+ TelemetryMetricRecord,
9
+ TelemetrySpanEnd,
10
+ TelemetrySpanEventRecord,
11
+ TelemetrySpanScope,
12
+ } from "../contracts/telemetryTypes";
13
+ import type { NodeActivationId, NodeId } from "../contracts/workflowTypes";
14
+
15
+ export class ExecutionTelemetryCostTrackingDecoratorFactory {
16
+ decorateExecutionTelemetry(args: {
17
+ telemetry: ExecutionTelemetry;
18
+ costTracking: CostTrackingTelemetry;
19
+ }): ExecutionTelemetry {
20
+ return {
21
+ traceId: args.telemetry.traceId,
22
+ spanId: args.telemetry.spanId,
23
+ costTracking: args.costTracking,
24
+ addSpanEvent: (event: TelemetrySpanEventRecord) => args.telemetry.addSpanEvent(event),
25
+ recordMetric: (metric: TelemetryMetricRecord) => args.telemetry.recordMetric(metric),
26
+ attachArtifact: (
27
+ artifact: TelemetryArtifactAttachment,
28
+ ): Promise<TelemetryArtifactReference> | TelemetryArtifactReference => args.telemetry.attachArtifact(artifact),
29
+ forNode: (nodeArgs: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry => {
30
+ const nodeTelemetry = args.telemetry.forNode(nodeArgs);
31
+ return this.decorateNodeExecutionTelemetry({
32
+ telemetry: nodeTelemetry,
33
+ costTracking: args.costTracking.forScope(nodeTelemetry),
34
+ });
35
+ },
36
+ };
37
+ }
38
+
39
+ private decorateNodeExecutionTelemetry(args: {
40
+ telemetry: NodeExecutionTelemetry;
41
+ costTracking: CostTrackingTelemetry;
42
+ }): NodeExecutionTelemetry {
43
+ return {
44
+ traceId: args.telemetry.traceId,
45
+ spanId: args.telemetry.spanId,
46
+ costTracking: args.costTracking,
47
+ addSpanEvent: (event: TelemetrySpanEventRecord) => args.telemetry.addSpanEvent(event),
48
+ recordMetric: (metric: TelemetryMetricRecord) => args.telemetry.recordMetric(metric),
49
+ attachArtifact: (
50
+ artifact: TelemetryArtifactAttachment,
51
+ ): Promise<TelemetryArtifactReference> | TelemetryArtifactReference => args.telemetry.attachArtifact(artifact),
52
+ end: (endArgs?: TelemetrySpanEnd) => args.telemetry.end(endArgs),
53
+ startChildSpan: (spanArgs: TelemetryChildSpanStart): TelemetrySpanScope =>
54
+ this.decorateTelemetrySpanScope({
55
+ scope: args.telemetry.startChildSpan(spanArgs),
56
+ costTracking: args.costTracking,
57
+ }),
58
+ forNode: (nodeArgs: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry => {
59
+ const nodeTelemetry = args.telemetry.forNode(nodeArgs);
60
+ return this.decorateNodeExecutionTelemetry({
61
+ telemetry: nodeTelemetry,
62
+ costTracking: args.costTracking.forScope(nodeTelemetry),
63
+ });
64
+ },
65
+ };
66
+ }
67
+
68
+ private decorateTelemetrySpanScope(args: {
69
+ scope: TelemetrySpanScope;
70
+ costTracking: CostTrackingTelemetry;
71
+ }): TelemetrySpanScope {
72
+ return {
73
+ traceId: args.scope.traceId,
74
+ spanId: args.scope.spanId,
75
+ costTracking: args.costTracking.forScope(args.scope),
76
+ addSpanEvent: (event: TelemetrySpanEventRecord) => args.scope.addSpanEvent(event),
77
+ recordMetric: (metric: TelemetryMetricRecord) => args.scope.recordMetric(metric),
78
+ attachArtifact: (
79
+ artifact: TelemetryArtifactAttachment,
80
+ ): Promise<TelemetryArtifactReference> | TelemetryArtifactReference => args.scope.attachArtifact(artifact),
81
+ end: (endArgs?: TelemetrySpanEnd) => args.scope.end(endArgs),
82
+ };
83
+ }
84
+ }
@@ -142,6 +142,7 @@ export class NodeActivationRequestComposer {
142
142
  nodeId: definition.id,
143
143
  activationId,
144
144
  config: definition.config,
145
+ telemetry: args.base.telemetry.forNode({ nodeId: definition.id, activationId }),
145
146
  binary: args.base.binary.forNode({ nodeId: definition.id, activationId }),
146
147
  getCredential: this.credentialResolverFactory.create(args.workflowId, definition.id, definition.config),
147
148
  };
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ JsonValue,
2
3
  NodeActivationId,
3
4
  NodeExecutionSnapshot,
4
5
  NodeId,
@@ -146,6 +147,7 @@ export class NodeExecutionSnapshotFactory {
146
147
  message: args.error.message,
147
148
  name: args.error.name,
148
149
  stack: args.error.stack,
150
+ details: (args.error as Error & { details?: JsonValue }).details,
149
151
  },
150
152
  };
151
153
  }
@@ -0,0 +1,22 @@
1
+ import type { CostCatalog, CostCatalogEntry } from "../contracts/CostCatalogContract";
2
+ import type { CostTrackingUsageRecord } from "../contracts/CostTrackingTelemetryContract";
3
+
4
+ export class StaticCostCatalog implements CostCatalog {
5
+ private readonly entriesByKey: ReadonlyMap<string, CostCatalogEntry>;
6
+
7
+ constructor(entries: ReadonlyArray<CostCatalogEntry>) {
8
+ this.entriesByKey = new Map(entries.map((entry) => [this.createKeyFromEntry(entry), entry]));
9
+ }
10
+
11
+ findEntry(args: CostTrackingUsageRecord): CostCatalogEntry | undefined {
12
+ return this.entriesByKey.get(this.createKeyFromUsage(args));
13
+ }
14
+
15
+ private createKeyFromEntry(entry: CostCatalogEntry): string {
16
+ return `${entry.component}::${entry.provider}::${entry.operation}::${entry.pricingKey}::${entry.usageUnit}`;
17
+ }
18
+
19
+ private createKeyFromUsage(args: CostTrackingUsageRecord): string {
20
+ return `${args.component}::${args.provider}::${args.operation}::${args.pricingKey}::${args.usageUnit}`;
21
+ }
22
+ }
@@ -24,6 +24,7 @@ export class WorkflowRunExecutionContextFactory {
24
24
  workflowId: WorkflowId;
25
25
  nodeId: NodeId;
26
26
  parent?: ParentExecutionRef;
27
+ policySnapshot?: import("../types").PersistedRunPolicySnapshot;
27
28
  subworkflowDepth: number;
28
29
  engineMaxNodeActivations: number;
29
30
  engineMaxSubworkflowDepth: number;
@@ -34,6 +35,7 @@ export class WorkflowRunExecutionContextFactory {
34
35
  runId: args.runId,
35
36
  workflowId: args.workflowId,
36
37
  parent: args.parent,
38
+ policySnapshot: args.policySnapshot,
37
39
  subworkflowDepth: args.subworkflowDepth,
38
40
  engineMaxNodeActivations: args.engineMaxNodeActivations,
39
41
  engineMaxSubworkflowDepth: args.engineMaxSubworkflowDepth,
@@ -4,6 +4,9 @@ export { NodeInputContractError } from "./NodeInputContractError";
4
4
  export { CredentialResolverFactory } from "./CredentialResolverFactory";
5
5
  export { DefaultAsyncSleeper } from "./DefaultAsyncSleeper";
6
6
  export { DefaultExecutionContextFactory } from "./DefaultExecutionContextFactory";
7
+ export { CatalogBackedCostTrackingTelemetry } from "./CatalogBackedCostTrackingTelemetry";
8
+ export { CatalogBackedCostTrackingTelemetryFactory } from "./CatalogBackedCostTrackingTelemetryFactory";
9
+ export { ExecutionTelemetryCostTrackingDecoratorFactory } from "./ExecutionTelemetryCostTrackingDecoratorFactory";
7
10
  export { InProcessRetryRunner } from "./InProcessRetryRunner";
8
11
  export { ItemExprResolver } from "./ItemExprResolver";
9
12
  export { NodeOutputNormalizer } from "./NodeOutputNormalizer";
@@ -21,6 +24,7 @@ export { NodeRunStateWriterFactory } from "./NodeRunStateWriterFactory";
21
24
  export { PersistedRunStateTerminalBuilder } from "./PersistedRunStateTerminalBuilder";
22
25
  export { RunStateSemantics } from "./RunStateSemantics";
23
26
  export { WorkflowRunExecutionContextFactory } from "./WorkflowRunExecutionContextFactory";
27
+ export { StaticCostCatalog } from "./StaticCostCatalog";
24
28
  export type { AsyncSleeper } from "./asyncSleeper.types";
25
29
  export { RunContinuationService } from "../orchestration/RunContinuationService";
26
30
  export { RunStartService } from "../orchestration/RunStartService";
@@ -71,6 +71,7 @@ export class NodeExecutionRequestHandlerService implements NodeExecutionRequestH
71
71
  workflowId: state.workflowId,
72
72
  nodeId: request.nodeId,
73
73
  parent: resolvedParent,
74
+ policySnapshot: state.policySnapshot,
74
75
  subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
75
76
  engineMaxNodeActivations: limits.engineMaxNodeActivations,
76
77
  engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
@@ -135,6 +135,7 @@ export class RunContinuationService {
135
135
  workflowId: state.workflowId,
136
136
  nodeId: args.nodeId,
137
137
  parent: state.parent,
138
+ policySnapshot: state.policySnapshot,
138
139
  subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
139
140
  engineMaxNodeActivations: limits.engineMaxNodeActivations,
140
141
  engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
@@ -748,6 +749,7 @@ export class RunContinuationService {
748
749
  workflowId: args.state.workflowId,
749
750
  nodeId: nextDefinition.id,
750
751
  parent: args.state.parent,
752
+ policySnapshot: args.state.policySnapshot,
751
753
  subworkflowDepth: args.state.executionOptions?.subworkflowDepth ?? 0,
752
754
  engineMaxNodeActivations: webhookLimits.engineMaxNodeActivations,
753
755
  engineMaxSubworkflowDepth: webhookLimits.engineMaxSubworkflowDepth,
@@ -851,6 +853,7 @@ export class RunContinuationService {
851
853
  workflowId: state.workflowId,
852
854
  nodeId,
853
855
  parent: state.parent,
856
+ policySnapshot: state.policySnapshot,
854
857
  subworkflowDepth: state.executionOptions?.subworkflowDepth ?? 0,
855
858
  engineMaxNodeActivations: limits.engineMaxNodeActivations,
856
859
  engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
@@ -864,6 +867,7 @@ export class RunContinuationService {
864
867
  nodeId,
865
868
  activationId,
866
869
  config: def.config,
870
+ telemetry: base.telemetry.forNode({ nodeId, activationId }),
867
871
  binary: base.binary.forNode({ nodeId, activationId }),
868
872
  getCredential: this.credentialResolverFactory.create(wf.id, nodeId, def.config),
869
873
  };
@@ -90,6 +90,7 @@ export class RunStartService {
90
90
  workflowId: workflow.id,
91
91
  nodeId: startAt,
92
92
  parent,
93
+ policySnapshot,
93
94
  subworkflowDepth: mergedExecutionOptions.subworkflowDepth ?? 0,
94
95
  engineMaxNodeActivations: mergedExecutionOptions.maxNodeActivations!,
95
96
  engineMaxSubworkflowDepth: mergedExecutionOptions.maxSubworkflowDepth!,
@@ -199,6 +200,7 @@ export class RunStartService {
199
200
  request.workflow,
200
201
  ) ?? "unknown_node",
201
202
  parent: request.parent,
203
+ policySnapshot,
202
204
  subworkflowDepth: mergedExecutionOptions.subworkflowDepth ?? 0,
203
205
  engineMaxNodeActivations: mergedExecutionOptions.maxNodeActivations!,
204
206
  engineMaxSubworkflowDepth: mergedExecutionOptions.maxSubworkflowDepth!,
@@ -10,6 +10,12 @@ export class RunPolicySnapshotFactory {
10
10
  const prune = workflow.prunePolicy;
11
11
  const retentionSeconds = prune?.runDataRetentionSeconds ?? defaults?.retentionSeconds;
12
12
  const binaryRetentionSeconds = prune?.binaryRetentionSeconds ?? defaults?.binaryRetentionSeconds;
13
+ const telemetrySpanRetentionSeconds =
14
+ prune?.telemetrySpanRetentionSeconds ?? defaults?.telemetrySpanRetentionSeconds;
15
+ const telemetryArtifactRetentionSeconds =
16
+ prune?.telemetryArtifactRetentionSeconds ?? defaults?.telemetryArtifactRetentionSeconds;
17
+ const telemetryMetricRetentionSeconds =
18
+ prune?.telemetryMetricRetentionSeconds ?? defaults?.telemetryMetricRetentionSeconds;
13
19
  const storagePolicy: WorkflowStoragePolicyMode =
14
20
  typeof workflow.storagePolicy === "string"
15
21
  ? (workflow.storagePolicy as WorkflowStoragePolicyMode)
@@ -17,6 +23,9 @@ export class RunPolicySnapshotFactory {
17
23
  return {
18
24
  retentionSeconds,
19
25
  binaryRetentionSeconds,
26
+ telemetrySpanRetentionSeconds,
27
+ telemetryArtifactRetentionSeconds,
28
+ telemetryMetricRetentionSeconds,
20
29
  storagePolicy,
21
30
  };
22
31
  }
@@ -87,14 +87,16 @@ export class InMemoryWorkflowExecutionRepository
87
87
  }
88
88
 
89
89
  async listRunsOlderThan(
90
- args: Readonly<{ beforeIso: string; limit?: number }>,
90
+ args: Readonly<{ nowIso: string; defaultRetentionSeconds: number; limit?: number }>,
91
91
  ): Promise<ReadonlyArray<RunPruneCandidate>> {
92
92
  const limit = args.limit ?? 100;
93
93
  const out: RunPruneCandidate[] = [];
94
94
  for (const s of this.runs.values()) {
95
95
  if (s.status !== "completed" && s.status !== "failed") continue;
96
96
  const finishedAt = RunFinishedAtFactory.resolveIso(s);
97
- if (!finishedAt || finishedAt >= args.beforeIso) continue;
97
+ const retentionSeconds = s.policySnapshot?.retentionSeconds ?? args.defaultRetentionSeconds;
98
+ const cutoffIso = new Date(new Date(args.nowIso).getTime() - retentionSeconds * 1000).toISOString();
99
+ if (!finishedAt || finishedAt >= cutoffIso) continue;
98
100
  out.push({
99
101
  runId: s.runId,
100
102
  workflowId: s.workflowId,
@@ -7,8 +7,13 @@ export * from "../contracts/NoRetryPolicy";
7
7
  export * from "../contracts/RetryPolicy";
8
8
  export * from "../contracts/ExpRetryPolicy";
9
9
  export * from "../contracts/credentialTypes";
10
+ export * from "../contracts/CostCatalogContract";
11
+ export * from "../contracts/CostTrackingTelemetryContract";
12
+ export * from "../contracts/NoOpCostTrackingTelemetry";
13
+ export * from "../contracts/NoOpCostTrackingTelemetryFactory";
10
14
  export * from "../contracts/executionPersistenceContracts";
11
15
  export * from "../contracts/runtimeTypes";
16
+ export * from "../contracts/telemetryTypes";
12
17
  export * from "../contracts/runFinishedAtFactory";
13
18
  export * from "../contracts/runTypes";
14
19
  export * from "../contracts/webhookTypes";