@codemation/host 0.8.0 → 0.9.1

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 (148) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/{ApiPaths-Dv1dcHu_.js → ApiPaths-DCvrlIjg.js} +12 -1
  3. package/dist/{ApiPaths-Dv1dcHu_.js.map → ApiPaths-DCvrlIjg.js.map} +1 -1
  4. package/dist/{AppConfigFactory-Cx4qQvRk.js → AppConfigFactory-D4LL1aOR.js} +77 -297
  5. package/dist/AppConfigFactory-D4LL1aOR.js.map +1 -0
  6. package/dist/{AppConfigFactory-BT0y0LVC.d.ts → AppConfigFactory-DncmwCD1.d.ts} +2918 -199
  7. package/dist/{AppContainerFactory-DRTjG7nG.js → AppContainerFactory-CHCXP2rn.js} +1735 -474
  8. package/dist/AppContainerFactory-CHCXP2rn.js.map +1 -0
  9. package/dist/{CodemationAppContext-CGFYVcSb.d.ts → CodemationAppContext-K51b7oXe.d.ts} +3 -3
  10. package/dist/{CodemationAuthoring.types-DiKKogum.d.ts → CodemationAuthoring.types-BXlXIl4K.d.ts} +4 -4
  11. package/dist/{CodemationConfigNormalizer-48f-T66P.d.ts → CodemationConfigNormalizer-B4rDYC9h.d.ts} +3 -3
  12. package/dist/{CodemationConsumerConfigLoader-_PIYqwVx.d.ts → CodemationConsumerConfigLoader-Dt4jyLx6.d.ts} +2 -2
  13. package/dist/{CodemationPluginListMerger-DP7djJ9S.d.ts → CodemationPluginListMerger-DS6I3Xe0.d.ts} +24 -12
  14. package/dist/{persistenceServer-C-hH4z6l.js → CodemationPostgresPrismaClientFactory-C7156Fe-.js} +2 -2
  15. package/dist/CodemationPostgresPrismaClientFactory-C7156Fe-.js.map +1 -0
  16. package/dist/CodemationPostgresPrismaClientFactory-CTNTPnDr.d.ts +9 -0
  17. package/dist/{CredentialContractsRegistry-Bq2bq28t.d.ts → CredentialContractsRegistry-Dgu-rEXi.d.ts} +16 -3
  18. package/dist/{CredentialServices-BLloBztI.d.ts → CredentialServices-B3wPyp2y.d.ts} +4 -4
  19. package/dist/{CredentialServices-Dk8yypeL.js → CredentialServices-Bios0dM8.js} +10 -4
  20. package/dist/CredentialServices-Bios0dM8.js.map +1 -0
  21. package/dist/{InternalHonoApiRouteRegistrar-c7t3KnV_.d.ts → InternalHonoApiRouteRegistrar-Ce1yxpnO.d.ts} +1 -1
  22. package/dist/{InternalPingRegistrar-DY3kSfxP.js → InternalPingRegistrar-BavAAnvk.js} +19 -16
  23. package/dist/InternalPingRegistrar-BavAAnvk.js.map +1 -0
  24. package/dist/{ItemsInputNormalizer-_RwIfRIQ.d.ts → ItemsInputNormalizer-CFkfNMLt.d.ts} +1434 -1225
  25. package/dist/PrismaMigrationDeployer-DdEcXXVi.d.ts +14 -0
  26. package/dist/{PublicFrontendBootstrapFactory-Dv04tJ-6.d.ts → PublicFrontendBootstrapFactory-ClEjZP74.d.ts} +2 -2
  27. package/dist/{PublicFrontendBootstrapJsonCodec-CXG9Dxft.d.ts → PublicFrontendBootstrapJsonCodec-HNItQ7ol.d.ts} +6 -1
  28. package/dist/{TelemetryContracts-BtDx84Cp.d.ts → TelemetryContracts-DpZEODQM.d.ts} +2 -2
  29. package/dist/{WorkflowPolicyUiPresentationFactory-6MyjCvBO.d.ts → WorkflowPolicyUiPresentationFactory-BNn2fvR_.d.ts} +2 -2
  30. package/dist/{WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js → WorkflowPolicyUiPresentationFactory-DfvD2VHk.js} +1 -1
  31. package/dist/{WorkflowPolicyUiPresentationFactory-Bb-ae_Zh.js.map → WorkflowPolicyUiPresentationFactory-DfvD2VHk.js.map} +1 -1
  32. package/dist/authoring.d.ts +4 -4
  33. package/dist/client.d.ts +1 -1
  34. package/dist/client.js +1 -1
  35. package/dist/consumer.d.ts +5 -5
  36. package/dist/credentials.d.ts +5 -5
  37. package/dist/credentials.js +1 -1
  38. package/dist/devServerSidecar.d.ts +2 -2
  39. package/dist/dto.d.ts +5 -5
  40. package/dist/{index-DilAYwnH.d.ts → index-ChIfeWzk.d.ts} +71 -28
  41. package/dist/index.d.ts +17 -16
  42. package/dist/index.js +8 -8
  43. package/dist/infrastructure/persistence/PrismaMigrationOperations.d.ts +44 -0
  44. package/dist/infrastructure/persistence/PrismaMigrationOperations.js +302 -0
  45. package/dist/infrastructure/persistence/PrismaMigrationOperations.js.map +1 -0
  46. package/dist/mapping.d.ts +2 -2
  47. package/dist/mapping.js +1 -1
  48. package/dist/nextServer.d.ts +15 -13
  49. package/dist/nextServer.js +6 -6
  50. package/dist/pairing.d.ts +28 -9
  51. package/dist/pairing.js +19 -3
  52. package/dist/pairing.js.map +1 -0
  53. package/dist/{pairing.types-snfZ_OzB.d.ts → pairing.types-D9Bjn98U.d.ts} +1 -1
  54. package/dist/persistenceServer.d.ts +31 -7
  55. package/dist/persistenceServer.js +2 -2
  56. package/dist/{server-09PKasWR.d.ts → server-B5trn7y4.d.ts} +5 -5
  57. package/dist/{server-vtRCPgRJ.js → server-CNj_y0QO.js} +4 -4
  58. package/dist/{server-vtRCPgRJ.js.map → server-CNj_y0QO.js.map} +1 -1
  59. package/dist/server.d.ts +10 -10
  60. package/dist/server.js +8 -8
  61. package/package.json +11 -10
  62. package/playwright.config.ts +8 -2
  63. package/playwright.scaffolded-dev.config.ts +8 -2
  64. package/prisma/migrations/20260526120000_credential_material_pointer/migration.sql +18 -0
  65. package/prisma/migrations/20260527120000_add_human_task/migration.sql +32 -0
  66. package/prisma/migrations/20260527130000_add_hitl_state_json/migration.sql +6 -0
  67. package/prisma/migrations/20260527130000_add_hmac_nonce/migration.sql +12 -0
  68. package/prisma/migrations.sqlite/20260526120000_credential_material_pointer/migration.sql +13 -0
  69. package/prisma/migrations.sqlite/20260527120000_add_human_task/migration.sql +30 -0
  70. package/prisma/migrations.sqlite/20260527130000_add_hitl_state_json/migration.sql +6 -0
  71. package/prisma/migrations.sqlite/20260527130000_add_hmac_nonce/migration.sql +9 -0
  72. package/prisma/schema.postgresql.prisma +48 -0
  73. package/prisma/schema.sqlite.prisma +48 -0
  74. package/prisma-generated/prisma-postgresql-client/edge.js +40 -6
  75. package/prisma-generated/prisma-postgresql-client/index-browser.js +36 -2
  76. package/prisma-generated/prisma-postgresql-client/index.d.ts +3179 -163
  77. package/prisma-generated/prisma-postgresql-client/index.js +40 -6
  78. package/prisma-generated/prisma-postgresql-client/package.json +1 -1
  79. package/prisma-generated/prisma-postgresql-client/schema.prisma +48 -0
  80. package/prisma-generated/prisma-sqlite-client/edge.js +40 -6
  81. package/prisma-generated/prisma-sqlite-client/index-browser.js +36 -2
  82. package/prisma-generated/prisma-sqlite-client/index.d.ts +3175 -163
  83. package/prisma-generated/prisma-sqlite-client/index.js +40 -6
  84. package/prisma-generated/prisma-sqlite-client/package.json +1 -1
  85. package/prisma-generated/prisma-sqlite-client/schema.prisma +48 -0
  86. package/src/application/contracts/CredentialContractsRegistry.ts +15 -0
  87. package/src/application/credentials/AppGalleryProjector.ts +69 -0
  88. package/src/application/hitl/DecideHumanTaskCommandHandler.ts +149 -0
  89. package/src/application/hitl/DecisionSchemaValidator.ts +22 -0
  90. package/src/application/hitl/HitlCallbackHandler.ts +96 -0
  91. package/src/application/mapping/WorkflowDefinitionMapper.ts +1 -3
  92. package/src/application/queries/CredentialQueryHandlers.ts +2 -0
  93. package/src/application/queries/GetCredentialAppsQuery.ts +4 -0
  94. package/src/application/queries/GetCredentialAppsQueryHandler.ts +27 -0
  95. package/src/application/telemetry/ResumeTelemetryContextForRun.ts +53 -0
  96. package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +9 -8
  97. package/src/applicationTokens.ts +11 -1
  98. package/src/auth/managed/ManagedCorsMiddleware.ts +20 -5
  99. package/src/bootstrap/AppContainerFactory.ts +100 -0
  100. package/src/credentials/CachingCredentialMaterialProvider.ts +96 -0
  101. package/src/credentials/CompositeCredentialMaterialProvider.ts +47 -0
  102. package/src/credentials/ControlPlaneCatalogFetcher.ts +4 -24
  103. package/src/credentials/ControlPlaneCredentialMaterialProvider.ts +79 -0
  104. package/src/credentials/CredentialOAuth2MaterialReader.ts +2 -7
  105. package/src/credentials/InternalCredentialsBindingRegistrar.ts +83 -0
  106. package/src/credentials/LocalCredentialMaterialProvider.ts +92 -0
  107. package/src/domain/credentials/CredentialInstanceService.ts +5 -1
  108. package/src/domain/credentials/CredentialTypeRegistryImpl.ts +18 -4
  109. package/src/domain/workflows/WorkflowActivationPreflightRules.ts +7 -4
  110. package/src/dto.ts +2 -0
  111. package/src/hitl/ControlPlaneInboxChannel.ts +102 -0
  112. package/src/hitl/HitlResumeTokenSigner.ts +80 -0
  113. package/src/hitl/HitlTimeoutJobScheduler.ts +89 -0
  114. package/src/hitl/HitlTimeoutWorker.ts +143 -0
  115. package/src/hitl/InboxChannelResolver.ts +49 -0
  116. package/src/hitl/LocalInboxChannel.ts +37 -0
  117. package/src/infrastructure/persistence/PrismaCredentialStore.ts +10 -0
  118. package/src/infrastructure/persistence/PrismaHmacNonceStore.ts +29 -0
  119. package/src/infrastructure/persistence/PrismaHumanTaskStore.ts +156 -0
  120. package/src/infrastructure/persistence/PrismaMigrationDeployer.ts +53 -383
  121. package/src/infrastructure/persistence/PrismaMigrationOperations.ts +401 -0
  122. package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +39 -0
  123. package/src/mcp/AgentMcpIntegrationImpl.ts +5 -1
  124. package/src/pairing/HmacNonceStore.ts +14 -0
  125. package/src/pairing/HmacNonceStoreToken.ts +4 -0
  126. package/src/pairing/HmacRequestSigner.ts +10 -1
  127. package/src/pairing/InMemoryHmacNonceStore.ts +24 -0
  128. package/src/pairing/IncomingHmacVerifier.ts +28 -12
  129. package/src/pairing/InternalHmacAuthMiddleware.ts +1 -1
  130. package/src/pairing/index.ts +3 -0
  131. package/src/presentation/http/ApiPaths.ts +14 -0
  132. package/src/presentation/http/hono/HonoHttpAnonymousRoutePolicyRegistry.ts +4 -0
  133. package/src/presentation/http/hono/registrars/CredentialHonoApiRouteRegistrar.ts +1 -0
  134. package/src/presentation/http/hono/registrars/HitlDecideHonoApiRouteRegistrar.ts +54 -0
  135. package/src/presentation/http/hono/registrars/HitlInternalCallbackHonoApiRouteRegistrar.ts +33 -0
  136. package/src/presentation/http/hono/registrars/HitlResumeHonoApiRouteRegistrar.ts +43 -0
  137. package/src/presentation/http/routeHandlers/CredentialHttpRouteHandler.ts +9 -0
  138. package/src/presentation/http/routeHandlers/OAuth2HttpRouteHandlerFactory.ts +1 -1
  139. package/src/server.ts +7 -2
  140. package/src/workflows/InternalWorkflowTestRunRegistrar.ts +9 -0
  141. package/tsconfig.json +1 -0
  142. package/dist/AppConfigFactory-Cx4qQvRk.js.map +0 -1
  143. package/dist/AppContainerFactory-DRTjG7nG.js.map +0 -1
  144. package/dist/CredentialServices-Dk8yypeL.js.map +0 -1
  145. package/dist/InternalPingRegistrar-DY3kSfxP.js.map +0 -1
  146. package/dist/persistenceServer-B71RGvSj.d.ts +0 -30
  147. package/dist/persistenceServer-C-hH4z6l.js.map +0 -1
  148. package/src/credentials/catalogTypes.ts +0 -4
@@ -1,8 +1,15 @@
1
1
  import "reflect-metadata";
2
2
  import { ReadableStream } from "node:stream/web";
3
- import { ZodType } from "zod";
3
+ import { ZodType, z } from "zod";
4
4
  import { DependencyContainer as Container, InjectionToken as TypeToken } from "tsyringe";
5
5
 
6
+ //#region ../core/src/contracts/testTriggerTypes.d.ts
7
+ /**
8
+ * Identifier minted by the host (or in-memory test runner) for one execution of a test suite.
9
+ * One TestSuiteRun produces N child workflow runs, one per item yielded by `generateItems`.
10
+ */
11
+ type TestSuiteRunId = string;
12
+ //#endregion
6
13
  //#region ../core/src/contracts/baseTypes.d.ts
7
14
  /**
8
15
  * Minimal base types that have no dependencies on other contracts.
@@ -16,1332 +23,1550 @@ type InputPortKey = string;
16
23
  type PersistedTokenId = string;
17
24
  type NodeConnectionName = string;
18
25
  //#endregion
19
- //#region ../core/src/contracts/credentialTypes.d.ts
20
- type CredentialTypeId = string;
21
- type CredentialInstanceId = string;
22
- type CredentialMaterialSourceKind = "db" | "env" | "code";
23
- type CredentialSetupStatus = "draft" | "ready";
24
- type CredentialHealthStatus = "unknown" | "healthy" | "failing";
25
- type CredentialFieldSchema = Readonly<{
26
- key: string;
27
- label: string;
28
- type: "string" | "password" | "textarea" | "json" | "boolean";
29
- required?: true;
30
- order?: number;
31
- /**
32
- * Where this field appears in the credential dialog. Use `"advanced"` for optional or
33
- * power-user fields; they render inside a collapsible section (see `CredentialTypeDefinition.advancedSection`).
34
- * Defaults to `"default"` when omitted.
35
- */
36
- visibility?: "default" | "advanced";
37
- placeholder?: string;
38
- helpText?: string;
39
- /** When set, host resolves this field from process.env at runtime; env wins over stored values. */
40
- envVarName?: string;
26
+ //#region ../core/src/contracts/CostTrackingTelemetryContract.d.ts
27
+ type CostTrackingComponent = "chat" | "ocr" | "rag";
28
+ interface CostTrackingUsageRecord {
29
+ readonly component: CostTrackingComponent;
30
+ readonly provider: string;
31
+ readonly operation: string;
32
+ readonly pricingKey: string;
33
+ readonly usageUnit: string;
34
+ readonly quantity: number;
35
+ readonly modelName?: string;
36
+ readonly attributes?: TelemetryAttributes;
37
+ }
38
+ interface CostTrackingPriceQuote {
39
+ readonly currency: string;
40
+ readonly currencyScale: number;
41
+ readonly estimatedAmountMinor: number;
42
+ readonly estimateKind: "catalog";
43
+ }
44
+ interface CostTrackingTelemetry {
45
+ captureUsage(args: CostTrackingUsageRecord): Promise<CostTrackingPriceQuote | undefined>;
46
+ forScope(scope: TelemetryScope): CostTrackingTelemetry;
47
+ }
48
+ //#endregion
49
+ //#region ../core/src/contracts/telemetryTypes.d.ts
50
+ type TelemetryAttributePrimitive = string | number | boolean | null;
51
+ interface TelemetryAttributes {
52
+ readonly [key: string]: TelemetryAttributePrimitive | undefined;
53
+ }
54
+ interface TelemetryMetricRecord {
55
+ readonly name: string;
56
+ readonly value: number;
57
+ readonly unit?: string;
58
+ readonly attributes?: TelemetryAttributes;
59
+ }
60
+ interface TelemetrySpanEventRecord {
61
+ readonly name: string;
62
+ readonly occurredAt?: Date;
63
+ readonly attributes?: TelemetryAttributes;
64
+ }
65
+ interface TelemetryArtifactAttachment {
66
+ readonly kind: string;
67
+ readonly contentType: string;
68
+ readonly previewText?: string;
69
+ readonly previewJson?: JsonValue;
70
+ readonly payloadText?: string;
71
+ readonly payloadJson?: JsonValue;
72
+ readonly bytes?: number;
73
+ readonly truncated?: boolean;
74
+ readonly expiresAt?: Date;
75
+ }
76
+ interface TelemetryArtifactReference {
77
+ readonly artifactId: string;
78
+ readonly traceId?: string;
79
+ readonly spanId?: string;
80
+ }
81
+ interface TelemetrySpanEnd {
82
+ readonly status?: "ok" | "error";
83
+ readonly statusMessage?: string;
84
+ readonly endedAt?: Date;
85
+ readonly attributes?: TelemetryAttributes;
86
+ }
87
+ interface TelemetryChildSpanStart {
88
+ readonly name: string;
89
+ readonly kind?: "internal" | "client";
90
+ readonly startedAt?: Date;
91
+ readonly attributes?: TelemetryAttributes;
92
+ }
93
+ interface TelemetryScope {
94
+ readonly traceId?: string;
95
+ readonly spanId?: string;
96
+ readonly costTracking?: CostTrackingTelemetry;
97
+ addSpanEvent(args: TelemetrySpanEventRecord): Promise<void> | void;
98
+ recordMetric(args: TelemetryMetricRecord): Promise<void> | void;
99
+ attachArtifact(args: TelemetryArtifactAttachment): Promise<TelemetryArtifactReference> | TelemetryArtifactReference;
100
+ }
101
+ interface TelemetrySpanScope extends TelemetryScope {
102
+ readonly traceId: string;
103
+ readonly spanId: string;
104
+ end(args?: TelemetrySpanEnd): Promise<void> | void;
41
105
  /**
42
- * When set, the dialog shows a copy action for this exact string (e.g. a static OAuth redirect URI
43
- * pattern or documentation URL). Do not use for secret values.
106
+ * Lift this span into a {@link NodeExecutionTelemetry} scoped to a different (nodeId, activationId).
107
+ * Children created via the returned telemetry's `startChildSpan` get this span as their parent.
108
+ *
109
+ * Used at the sub-agent boundary so that nested runtime telemetry parents under the agent.tool.call
110
+ * span instead of the orchestrator's node-level span.
44
111
  */
45
- copyValue?: string;
46
- /** Accessible label for the copy control (default: Copy). */
47
- copyButtonLabel?: string;
48
- }>;
49
- type CredentialRequirement = Readonly<{
50
- slotKey: string;
51
- label: string;
52
- acceptedTypes: ReadonlyArray<CredentialTypeId>;
53
- optional?: true;
54
- helpText?: string;
55
- helpUrl?: string;
56
- }>;
57
- type CredentialBindingKey = Readonly<{
112
+ asNodeTelemetry(args: Readonly<{
113
+ nodeId: NodeId;
114
+ activationId: NodeActivationId;
115
+ }>): NodeExecutionTelemetry;
116
+ }
117
+ interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
118
+ startChildSpan(args: TelemetryChildSpanStart): TelemetrySpanScope;
119
+ }
120
+ interface ExecutionTelemetry extends TelemetryScope {
121
+ readonly traceId: string;
122
+ readonly spanId: string;
123
+ forNode(args: Readonly<{
124
+ nodeId: NodeId;
125
+ activationId: NodeActivationId;
126
+ }>): NodeExecutionTelemetry;
127
+ }
128
+ //#endregion
129
+ //#region ../core/src/contracts/itemExpr.d.ts
130
+ declare const ITEM_EXPR_BRAND: unique symbol;
131
+ type ItemExprResolvedContext = Readonly<{
132
+ runId: RunId;
58
133
  workflowId: WorkflowId;
59
134
  nodeId: NodeId;
60
- slotKey: string;
61
- }>;
62
- type CredentialBinding = Readonly<{
63
- key: CredentialBindingKey;
64
- instanceId: CredentialInstanceId;
65
- updatedAt: string;
66
- }>;
67
- type CredentialHealth = Readonly<{
68
- status: CredentialHealthStatus;
69
- message?: string;
70
- testedAt?: string;
71
- expiresAt?: string;
72
- details?: Readonly<Record<string, unknown>>;
73
- }>;
74
- type OAuth2ProviderFromPublicConfig = Readonly<{
75
- authorizeUrlFieldKey: string;
76
- tokenUrlFieldKey: string;
77
- userInfoUrlFieldKey?: string;
78
- }>;
79
- type CredentialOAuth2ScopesFromPublicConfig = Readonly<{
80
- presetFieldKey: string;
81
- presetScopes: Readonly<Record<string, ReadonlyArray<string>>>;
82
- customPresetKey?: string;
83
- customScopesFieldKey?: string;
84
- }>;
85
- type CredentialOAuth2AuthDefinition = Readonly<{
86
- kind: "oauth2";
87
- providerId: string;
88
- scopes: ReadonlyArray<string>;
89
- scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
90
- clientIdFieldKey?: string;
91
- clientSecretFieldKey?: string;
92
- } | {
93
- kind: "oauth2";
94
- providerFromPublicConfig: OAuth2ProviderFromPublicConfig;
95
- scopes: ReadonlyArray<string>;
96
- scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
97
- clientIdFieldKey?: string;
98
- clientSecretFieldKey?: string;
99
- } | {
100
- kind: "oauth2";
101
- /**
102
- * Free-form provider identifier for telemetry, DB rows, and Better Auth provider naming.
103
- * Not used for any registry lookup — URLs come from {@link authorizeUrl} / {@link tokenUrl}.
104
- */
105
- providerId: string;
106
- /**
107
- * Authorization endpoint. May contain `{publicFieldKey}` placeholders that the runtime
108
- * substitutes from the credential's resolved public config (URL-encoded).
109
- * Example: `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize`
110
- */
111
- authorizeUrl: string;
112
- /** Token endpoint. Same templating rules as {@link authorizeUrl}. */
113
- tokenUrl: string;
114
- /** Optional userinfo endpoint. Same templating rules as {@link authorizeUrl}. */
115
- userInfoUrl?: string;
116
- scopes: ReadonlyArray<string>;
117
- scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
118
- clientIdFieldKey?: string;
119
- clientSecretFieldKey?: string;
135
+ activationId: NodeActivationId;
136
+ data: RunDataSnapshot;
120
137
  }>;
121
- type CredentialAuthDefinition = CredentialOAuth2AuthDefinition;
122
- type CredentialAdvancedSectionPresentation = Readonly<{
123
- /** Collapsible section title (default: "Advanced"). */
124
- title?: string;
125
- /** Optional short helper text shown inside the section (above the fields). */
126
- description?: string;
127
- /** When true, the advanced section starts expanded. Default: false (collapsed). */
128
- defaultOpen?: boolean;
138
+ /**
139
+ * Context aligned with former {@link ItemInputMapperContext} — use **`data`** to read any completed upstream node.
140
+ */
141
+ type ItemExprContext = ItemExprResolvedContext;
142
+ type ItemExprArgs<TItemJson = unknown> = Readonly<{
143
+ item: Item<TItemJson>;
144
+ itemIndex: number;
145
+ items: Items<TItemJson>;
146
+ ctx: ItemExprContext;
129
147
  }>;
130
- type CredentialTypeDefinition = Readonly<{
131
- typeId: CredentialTypeId;
132
- displayName: string;
133
- description?: string;
134
- publicFields?: ReadonlyArray<CredentialFieldSchema>;
135
- secretFields?: ReadonlyArray<CredentialFieldSchema>;
136
- /**
137
- * Optional labels for the collapsible block that contains every field with `visibility: "advanced"`.
138
- * If omitted, the UI still shows that block with defaults (title "Advanced", collapsed).
139
- */
140
- advancedSection?: CredentialAdvancedSectionPresentation;
141
- supportedSourceKinds?: ReadonlyArray<CredentialMaterialSourceKind>;
142
- auth?: CredentialAuthDefinition;
148
+ type ItemExprCallback<T, TItemJson = unknown> = (args: ItemExprArgs<TItemJson>) => T | Promise<T>;
149
+ type ItemExpr<T, TItemJson = unknown> = Readonly<{
150
+ readonly [ITEM_EXPR_BRAND]: true;
151
+ readonly fn: ItemExprCallback<T, TItemJson>;
143
152
  }>;
153
+ //#endregion
154
+ //#region ../core/src/contracts/params.d.ts
155
+ type Expr<T, TItemJson = unknown> = ItemExpr<T, TItemJson>;
156
+ type ParamDeep<T, TItemJson = unknown> = Expr<T, TItemJson> | (T extends readonly (infer U)[] ? ReadonlyArray<ParamDeep<U, TItemJson>> : never) | (T extends object ? { [K in keyof T]: ParamDeep<T[K], TItemJson> } : T);
157
+ //#endregion
158
+ //#region ../core/src/contracts/retryPolicySpec.types.d.ts
144
159
  /**
145
- * JSON-shaped credential field bag (public config, resolved secret material, etc.).
160
+ * In-process retry policy for runnable nodes. Serialized configs use the same
161
+ * `kind` discriminator (`JSON.stringify` / persisted workflows).
162
+ *
163
+ * `maxAttempts` is the total number of tries including the first (e.g. 3 means up to 2 delays after failures).
146
164
  */
147
- type CredentialJsonRecord = Readonly<Record<string, unknown>>;
148
- /**
149
- * Persisted credential instance with typed `publicConfig`.
150
- * Hosts may specialize `secretRef` with a stricter union while remaining
151
- * assignable here for session/test callbacks.
152
- */
153
- type CredentialInstanceRecord<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord> = Readonly<{
154
- instanceId: CredentialInstanceId;
155
- typeId: CredentialTypeId;
156
- displayName: string;
157
- sourceKind: CredentialMaterialSourceKind;
158
- publicConfig: TPublicConfig;
159
- secretRef: CredentialJsonRecord;
160
- tags: ReadonlyArray<string>;
161
- setupStatus: CredentialSetupStatus;
162
- createdAt: string;
163
- updatedAt: string;
164
- }>;
165
- /**
166
- * Arguments passed to `CredentialType.createSession` and `CredentialType.test`.
167
- * Declare `TPublicConfig` / `TMaterial` on `CredentialType` so implementations are checked
168
- * against your credential shapes (similar to `NodeExecutionContext.config` for nodes).
169
- */
170
- type CredentialSessionFactoryArgs<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord> = Readonly<{
171
- instance: CredentialInstanceRecord<TPublicConfig>;
172
- material: TMaterial;
173
- publicConfig: TPublicConfig;
174
- }>;
175
- type CredentialSessionFactory<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord, TSession = unknown> = (args: CredentialSessionFactoryArgs<TPublicConfig, TMaterial>) => Promise<TSession>;
176
- type CredentialHealthTester<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord> = (args: CredentialSessionFactoryArgs<TPublicConfig, TMaterial>) => Promise<CredentialHealth>;
177
- /**
178
- * Full credential type implementation: `definition` (UI/schema), `createSession`, and `test`.
179
- * Use this at registration and config boundaries; `CredentialTypeDefinition` is only the schema slice.
180
- */
181
- type CredentialType<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord, TSession = unknown> = Readonly<{
182
- definition: CredentialTypeDefinition;
183
- createSession: CredentialSessionFactory<TPublicConfig, TMaterial, TSession>;
184
- test: CredentialHealthTester<TPublicConfig, TMaterial>;
185
- }>;
186
- /**
187
- * Credential type with unspecified generics — used for `CodemationConfig.credentialTypes`, the host registry,
188
- * and anywhere a concrete `CredentialType<YourPublic, YourMaterial, YourSession>` is placed in a heterogeneous list.
189
- * Using `any` here avoids unsafe `as` casts while keeping typed `satisfies CredentialType<…>` definitions.
190
- */
191
- type AnyCredentialType = CredentialType<any, any, unknown>;
192
- interface CredentialSessionService {
193
- getSession<TSession = unknown>(args: Readonly<{
194
- workflowId: WorkflowId;
195
- nodeId: NodeId;
196
- slotKey: string;
197
- }>): Promise<TSession>;
165
+ type RetryPolicySpec = NoneRetryPolicySpec | FixedRetryPolicySpec | ExponentialRetryPolicySpec;
166
+ interface NoneRetryPolicySpec {
167
+ readonly kind: "none";
198
168
  }
199
- interface CredentialTypeRegistry {
200
- listTypes(): ReadonlyArray<CredentialTypeDefinition>;
201
- getType(typeId: CredentialTypeId): CredentialTypeDefinition | undefined;
169
+ interface FixedRetryPolicySpec {
170
+ readonly kind: "fixed";
171
+ /** Total attempts including the first execution. Must be >= 1. */
172
+ readonly maxAttempts: number;
173
+ readonly delayMs: number;
174
+ }
175
+ interface ExponentialRetryPolicySpec {
176
+ readonly kind: "exponential";
177
+ /** Total attempts including the first execution. Must be >= 1. */
178
+ readonly maxAttempts: number;
179
+ readonly initialDelayMs: number;
180
+ readonly multiplier: number;
181
+ readonly maxDelayMs?: number;
182
+ /** When true, each delay is multiplied by a random factor in [1, 1.2). */
183
+ readonly jitter?: boolean;
202
184
  }
203
185
  //#endregion
204
- //#region ../core/src/contracts/runTypes.d.ts
186
+ //#region ../core/src/contracts/executionPersistenceContracts.d.ts
187
+ /** Canonical id for persisted execution rows (activation or connection invocation). */
188
+ type ExecutionInstanceId = string;
189
+ /** Batch grouping for planner activations. */
190
+ type BatchId = string;
191
+ type PersistedExecutionInstanceKind = "workflowNodeActivation" | "connectionInvocation";
192
+ type ConnectionInvocationKind = "languageModel" | "tool" | "nestedAgent";
193
+ interface WorkflowRunDetailDto {
194
+ readonly runId: RunId;
195
+ readonly workflowId: WorkflowId;
196
+ readonly startedAt: string;
197
+ readonly finishedAt?: string;
198
+ readonly status: RunStatus;
199
+ readonly workflowSnapshot?: PersistedWorkflowSnapshot;
200
+ readonly mutableState?: PersistedMutableRunState;
201
+ readonly slotStates: ReadonlyArray<SlotExecutionStateDto>;
202
+ readonly executionInstances: ReadonlyArray<ExecutionInstanceDto>;
203
+ readonly iterations?: ReadonlyArray<RunIterationDto>;
204
+ }
205
205
  /**
206
- * Test-suite linkage for a run. When set, this run was started by a TestSuiteOrchestrator
207
- * as one test case inside a TestSuiteRun. The `IsTestRun` node and host-side persisters key
208
- * off the presence of this field. Subworkflow runs inherit it from their parent run.
206
+ * Per-item iteration projected from connection invocations and node activations.
207
+ *
208
+ * One iteration = one item processed by an agent within an activation. Multiple invocations
209
+ * (LLM rounds, tool calls) belonging to the same iteration share the iterationId.
209
210
  */
210
- interface RunTestContext {
211
- readonly testSuiteRunId: string;
212
- readonly testCaseIndex: number;
213
- /**
214
- * Optional human-friendly label for this test case (e.g. an email subject when fixtures
215
- * are loaded from a mailbox). Resolved per item by `TestTrigger.caseLabel(item)` if set,
216
- * persisted on `Run.test_case_label` so the Tests-tab tree-table can show "RFQ for batch 14"
217
- * instead of "run_1777755971399_bbb86beac1396".
218
- */
219
- readonly testCaseLabel?: string;
211
+ interface RunIterationDto {
212
+ readonly iterationId: string;
213
+ readonly agentNodeId: NodeId;
214
+ readonly activationId: NodeActivationId;
215
+ readonly itemIndex: number;
216
+ readonly itemSummary?: string;
217
+ readonly status: NodeExecutionStatus;
218
+ readonly startedAt?: string;
219
+ readonly finishedAt?: string;
220
+ readonly invocationIds: ReadonlyArray<string>;
221
+ readonly parentInvocationId?: string;
222
+ /** Estimated cost rolled up from telemetry cost metric points, keyed by ISO currency code (e.g. "USD"). Values are minor units (cents-of-cents per the metric's `cost.currency_scale`). */
223
+ readonly estimatedCostMinorByCurrency?: Readonly<Record<string, number>>;
224
+ /** Currency scale (denominator) per currency, when present on the metric points. Joined with `estimatedCostMinorByCurrency` to format human-readable amounts. */
225
+ readonly estimatedCostCurrencyScaleByCurrency?: Readonly<Record<string, number>>;
220
226
  }
221
- interface RunExecutionOptions {
222
- /** Run-intent override: force the inline scheduler and bypass node-level offload decisions. */
223
- localOnly?: boolean;
224
- /** Marks runs started from webhook handling so orchestration can apply webhook-specific continuation rules. */
225
- webhook?: boolean;
226
- mode?: "manual" | "debug";
227
- sourceWorkflowId?: WorkflowId;
228
- sourceRunId?: RunId;
229
- derivedFromRunId?: RunId;
230
- isMutable?: boolean;
231
- /** Set by the engine for this run: 0 = root, 1 = first child subworkflow, … */
232
- subworkflowDepth?: number;
233
- /** Effective cap after engine policy merge (successful node completions per run). */
234
- maxNodeActivations?: number;
235
- /** Effective cap after engine policy merge (subworkflow nesting). */
236
- maxSubworkflowDepth?: number;
237
- /** Present iff started by a TestSuiteOrchestrator; propagates to subworkflow runs via {@link ParentExecutionRef.testContext}. */
238
- testContext?: RunTestContext;
227
+ interface SlotExecutionStateDto {
228
+ readonly slotNodeId: NodeId;
229
+ readonly latestInstanceId?: ExecutionInstanceId;
230
+ readonly latestTerminalInstanceId?: ExecutionInstanceId;
231
+ readonly latestRunningInstanceId?: ExecutionInstanceId;
232
+ readonly status?: NodeExecutionStatus;
233
+ readonly invocationCount: number;
234
+ readonly runCount: number;
239
235
  }
240
- /** Engine-owned counters persisted with the run (worker-safe). */
241
- interface EngineRunCounters {
242
- completedNodeActivations: number;
236
+ interface ExecutionInstanceDto {
237
+ readonly instanceId: ExecutionInstanceId;
238
+ readonly slotNodeId: NodeId;
239
+ readonly workflowNodeId: NodeId;
240
+ readonly parentInstanceId?: ExecutionInstanceId;
241
+ readonly kind: PersistedExecutionInstanceKind;
242
+ readonly connectionKind?: ConnectionInvocationKind;
243
+ readonly runIndex: number;
244
+ readonly batchId: BatchId;
245
+ readonly activationId?: NodeActivationId;
246
+ readonly status: NodeExecutionStatus;
247
+ readonly queuedAt?: string;
248
+ readonly startedAt?: string;
249
+ readonly finishedAt?: string;
250
+ readonly itemCount: number;
251
+ readonly inputJson?: JsonValue;
252
+ readonly outputJson?: JsonValue;
253
+ readonly error?: Readonly<NodeExecutionError>;
254
+ /** Per-item iteration that produced this instance. Set on connectionInvocation rows produced inside per-item runnable loops. */
255
+ readonly iterationId?: string;
256
+ /** Item index (0-based) of the iteration. */
257
+ readonly itemIndex?: number;
258
+ /** Parent invocation id when this instance was emitted by a sub-agent triggered by an outer LLM/tool call. */
259
+ readonly parentInvocationId?: string;
260
+ /**
261
+ * When this instance is a SubWorkflow node activation, the run id of the child run it spawned.
262
+ * Used by the UI to deep-link directly to the child execution.
263
+ */
264
+ readonly childRunId?: string;
243
265
  }
244
- type RunStopCondition = Readonly<{
245
- kind: "workflowCompleted";
246
- }> | Readonly<{
247
- kind: "nodeCompleted";
266
+ //#endregion
267
+ //#region ../core/src/contracts/webhookTypes.d.ts
268
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
269
+ /** Match for an incoming HTTP request: user-defined URL segment + workflow trigger node. */
270
+ interface WebhookInvocationMatch {
271
+ /** Same value as the webhook trigger's configured endpoint key (URL segment under the webhook base path). */
272
+ endpointPath: string;
273
+ workflowId: WorkflowId;
248
274
  nodeId: NodeId;
249
- }>;
250
- interface RunStateResetRequest {
251
- clearFromNodeId: NodeId;
252
- }
253
- interface PersistedRunControlState {
254
- stopCondition?: RunStopCondition;
255
- }
256
- interface PersistedWorkflowSnapshotNode {
257
- id: NodeId;
258
- kind: NodeKind;
259
- name?: string;
260
- nodeTokenId: PersistedTokenId;
261
- configTokenId: PersistedTokenId;
262
- tokenName?: string;
263
- configTokenName?: string;
264
- config: unknown;
265
- /** Pre-computed static configuration summary; populated by WorkflowSnapshotCodec. */
266
- inspectorSummary?: ReadonlyArray<Readonly<{
267
- label: string;
268
- value: string;
269
- }>>;
270
- }
271
- interface PersistedWorkflowSnapshot {
272
- id: WorkflowId;
273
- name: string;
274
- nodes: ReadonlyArray<PersistedWorkflowSnapshotNode>;
275
- edges: ReadonlyArray<Edge>;
276
- /** When the snapshot was built from a live workflow definition that configured a workflow error handler. */
277
- workflowErrorHandlerConfigured?: boolean;
278
- /** Connection metadata for child nodes not in the execution graph (e.g. AI agent attachments). */
279
- connections?: ReadonlyArray<WorkflowNodeConnection>;
275
+ methods: ReadonlyArray<HttpMethod>;
276
+ parseJsonBody?: (body: unknown) => unknown;
280
277
  }
281
- type PinnedNodeOutputsByPort = Readonly<Record<OutputPortKey, Items>>;
282
- interface PersistedMutableNodeState {
283
- pinnedOutputsByPort?: PinnedNodeOutputsByPort;
284
- lastDebugInput?: Items;
278
+ /** Result of resolving an HTTP method + endpoint path against the catalog webhook index (404 vs 405 vs match). */
279
+ type WebhookTriggerResolution = {
280
+ status: "notFound";
281
+ } | {
282
+ status: "methodNotAllowed";
283
+ match: WebhookInvocationMatch;
284
+ } | {
285
+ status: "ok";
286
+ match: WebhookInvocationMatch;
287
+ };
288
+ /**
289
+ * Resolves webhook routes from workflow definitions (catalog-backed index, no registration at trigger setup).
290
+ */
291
+ interface WebhookTriggerMatcher {
292
+ match(args: {
293
+ endpointPath: string;
294
+ method: HttpMethod;
295
+ }): WebhookInvocationMatch | undefined;
296
+ lookup(endpointPath: string): WebhookInvocationMatch | undefined;
297
+ onEngineWorkflowsLoaded?(): void;
298
+ onEngineStopped?(): void;
299
+ /** Rebuild route index after activation changes without stopping the engine. */
300
+ reloadWebhookRoutes?(): void;
285
301
  }
286
- interface PersistedMutableRunState {
287
- nodesById: Readonly<Record<NodeId, PersistedMutableNodeState>>;
288
- }
289
- type NodeInputsByPort = Readonly<Record<InputPortKey, Items>>;
290
- interface RunQueueEntry {
291
- nodeId: NodeId;
292
- input: Items;
293
- toInput?: InputPortKey;
294
- batchId?: string;
295
- from?: Readonly<{
296
- nodeId: NodeId;
297
- output: OutputPortKey;
298
- }>;
299
- collect?: Readonly<{
300
- expectedInputs: ReadonlyArray<InputPortKey>;
301
- received: Readonly<Record<InputPortKey, Items>>;
302
- }>;
303
- }
304
- type NodeExecutionStatus = "pending" | "queued" | "running" | "completed" | "failed" | "skipped";
305
- interface NodeExecutionError {
306
- message: string;
307
- name?: string;
308
- stack?: string;
309
- details?: JsonValue;
310
- }
311
- interface NodeExecutionSnapshot {
312
- runId: RunId;
302
+ //#endregion
303
+ //#region ../core/src/contracts/credentialTypes.d.ts
304
+ type CredentialTypeId = string;
305
+ type CredentialInstanceId = string;
306
+ type CredentialMaterialSourceKind = "db" | "env" | "code";
307
+ type CredentialSetupStatus = "draft" | "ready";
308
+ type CredentialHealthStatus = "unknown" | "healthy" | "failing";
309
+ type CredentialFieldSchema = Readonly<{
310
+ key: string;
311
+ label: string;
312
+ type: "string" | "password" | "textarea" | "json" | "boolean";
313
+ required?: true;
314
+ order?: number;
315
+ /**
316
+ * Where this field appears in the credential dialog. Use `"advanced"` for optional or
317
+ * power-user fields; they render inside a collapsible section (see `CredentialTypeDefinition.advancedSection`).
318
+ * Defaults to `"default"` when omitted.
319
+ */
320
+ visibility?: "default" | "advanced";
321
+ placeholder?: string;
322
+ helpText?: string;
323
+ /** When set, host resolves this field from process.env at runtime; env wins over stored values. */
324
+ envVarName?: string;
325
+ /**
326
+ * When set, the dialog shows a copy action for this exact string (e.g. a static OAuth redirect URI
327
+ * pattern or documentation URL). Do not use for secret values.
328
+ */
329
+ copyValue?: string;
330
+ /** Accessible label for the copy control (default: Copy). */
331
+ copyButtonLabel?: string;
332
+ }>;
333
+ type CredentialRequirement = Readonly<{
334
+ slotKey: string;
335
+ label: string;
336
+ acceptedTypes: ReadonlyArray<CredentialTypeId>;
337
+ optional?: true;
338
+ helpText?: string;
339
+ helpUrl?: string;
340
+ }>;
341
+ type CredentialBindingKey = Readonly<{
313
342
  workflowId: WorkflowId;
314
343
  nodeId: NodeId;
315
- activationId?: NodeActivationId;
316
- parent?: ParentExecutionRef;
317
- status: NodeExecutionStatus;
318
- usedPinnedOutput?: boolean;
319
- queuedAt?: string;
320
- startedAt?: string;
321
- finishedAt?: string;
344
+ slotKey: string;
345
+ }>;
346
+ type CredentialBinding = Readonly<{
347
+ key: CredentialBindingKey;
348
+ instanceId: CredentialInstanceId;
322
349
  updatedAt: string;
323
- inputsByPort?: NodeInputsByPort;
324
- outputs?: NodeOutputs;
325
- error?: NodeExecutionError;
350
+ }>;
351
+ type CredentialHealth = Readonly<{
352
+ status: CredentialHealthStatus;
353
+ message?: string;
354
+ testedAt?: string;
355
+ expiresAt?: string;
356
+ details?: Readonly<Record<string, unknown>>;
357
+ }>;
358
+ type OAuth2ProviderFromPublicConfig = Readonly<{
359
+ authorizeUrlFieldKey: string;
360
+ tokenUrlFieldKey: string;
361
+ userInfoUrlFieldKey?: string;
362
+ }>;
363
+ type CredentialOAuth2ScopesFromPublicConfig = Readonly<{
364
+ presetFieldKey: string;
365
+ presetScopes: Readonly<Record<string, ReadonlyArray<string>>>;
366
+ customPresetKey?: string;
367
+ customScopesFieldKey?: string;
368
+ }>;
369
+ type CredentialOAuth2AuthDefinition = Readonly<{
370
+ kind: "oauth2";
371
+ providerId: string;
372
+ scopes: ReadonlyArray<string>;
373
+ scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
374
+ clientIdFieldKey?: string;
375
+ clientSecretFieldKey?: string;
376
+ } | {
377
+ kind: "oauth2";
378
+ providerFromPublicConfig: OAuth2ProviderFromPublicConfig;
379
+ scopes: ReadonlyArray<string>;
380
+ scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
381
+ clientIdFieldKey?: string;
382
+ clientSecretFieldKey?: string;
383
+ } | {
384
+ kind: "oauth2";
326
385
  /**
327
- * When the node is a SubWorkflow invocation, the run id of the child run it spawned.
328
- * Populated after the child run completes so the UI can deep-link to that specific execution.
386
+ * Free-form provider identifier for telemetry, DB rows, and Better Auth provider naming.
387
+ * Not used for any registry lookup URLs come from {@link authorizeUrl} / {@link tokenUrl}.
329
388
  */
330
- childRunId?: RunId;
331
- }
332
- /** Stable id for a single connection invocation row in {@link ConnectionInvocationRecord}. */
333
- type ConnectionInvocationId = string;
389
+ providerId: string;
390
+ /**
391
+ * Authorization endpoint. May contain `{publicFieldKey}` placeholders that the runtime
392
+ * substitutes from the credential's resolved public config (URL-encoded).
393
+ * Example: `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize`
394
+ */
395
+ authorizeUrl: string;
396
+ /** Token endpoint. Same templating rules as {@link authorizeUrl}. */
397
+ tokenUrl: string;
398
+ /** Optional userinfo endpoint. Same templating rules as {@link authorizeUrl}. */
399
+ userInfoUrl?: string;
400
+ scopes: ReadonlyArray<string>;
401
+ scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
402
+ clientIdFieldKey?: string;
403
+ clientSecretFieldKey?: string;
404
+ }>;
405
+ type CredentialAuthDefinition = CredentialOAuth2AuthDefinition;
406
+ type CredentialAdvancedSectionPresentation = Readonly<{
407
+ /** Collapsible section title (default: "Advanced"). */
408
+ title?: string;
409
+ /** Optional short helper text shown inside the section (above the fields). */
410
+ description?: string;
411
+ /** When true, the advanced section starts expanded. Default: false (collapsed). */
412
+ defaultOpen?: boolean;
413
+ }>;
414
+ type CredentialTypeDefinition = Readonly<{
415
+ typeId: CredentialTypeId;
416
+ displayName: string;
417
+ description?: string;
418
+ publicFields?: ReadonlyArray<CredentialFieldSchema>;
419
+ secretFields?: ReadonlyArray<CredentialFieldSchema>;
420
+ /**
421
+ * Optional labels for the collapsible block that contains every field with `visibility: "advanced"`.
422
+ * If omitted, the UI still shows that block with defaults (title "Advanced", collapsed).
423
+ */
424
+ advancedSection?: CredentialAdvancedSectionPresentation;
425
+ supportedSourceKinds?: ReadonlyArray<CredentialMaterialSourceKind>;
426
+ auth?: CredentialAuthDefinition;
427
+ }>;
334
428
  /**
335
- * One logical LLM or tool call under an owning workflow node (e.g. AI agent).
336
- * The owning node defines what {@link managedInput} and {@link managedOutput} contain.
429
+ * JSON-shaped credential field bag (public config, resolved secret material, etc.).
337
430
  */
338
- interface ConnectionInvocationRecord {
339
- readonly invocationId: ConnectionInvocationId;
340
- readonly runId: RunId;
341
- readonly workflowId: WorkflowId;
342
- readonly connectionNodeId: NodeId;
343
- readonly parentAgentNodeId: NodeId;
344
- readonly parentAgentActivationId: NodeActivationId;
345
- readonly status: NodeExecutionStatus;
346
- readonly managedInput?: JsonValue;
347
- readonly managedOutput?: JsonValue;
348
- /** Short human-readable description of what this invocation is doing right now (e.g. `"calling search_messages"`). Rendered as a sub-line on the canvas node card. */
349
- readonly statusLabel?: string;
350
- /** Stable identifier for the thing this invocation acts on (e.g. an MCP tool name like `"search_messages"`). Persists across status transitions so the inspector can show it on completed/failed entries too. Connection nodes that ARE the tool (e.g. node-backed agent tools) leave this unset — the parent node id already identifies the subject. */
351
- readonly subjectName?: string;
352
- readonly error?: NodeExecutionError;
353
- readonly queuedAt?: string;
354
- readonly startedAt?: string;
355
- readonly finishedAt?: string;
356
- readonly updatedAt: string;
357
- /** Per-item iteration id minted by the engine when this invocation occurred inside a runnable node's per-item loop. */
358
- readonly iterationId?: NodeIterationId;
359
- /** Item index (0-based) of the iteration that produced this invocation. */
360
- readonly itemIndex?: number;
361
- /** When set, this invocation was produced inside a sub-agent triggered by the named parent invocation. */
362
- readonly parentInvocationId?: ConnectionInvocationId;
363
- }
364
- /** Arguments for appending a {@link ConnectionInvocationRecord} (engine fills run/workflow ids and timestamps). */
365
- type ConnectionInvocationAppendArgs = Readonly<{
366
- invocationId: ConnectionInvocationId;
367
- connectionNodeId: NodeId;
368
- parentAgentNodeId: NodeId;
369
- parentAgentActivationId: NodeActivationId;
370
- status: NodeExecutionStatus;
371
- managedInput?: JsonValue;
372
- managedOutput?: JsonValue;
373
- statusLabel?: string;
374
- subjectName?: string;
375
- error?: NodeExecutionError;
376
- queuedAt?: string;
377
- startedAt?: string;
378
- finishedAt?: string;
379
- iterationId?: NodeIterationId;
380
- itemIndex?: number;
381
- parentInvocationId?: ConnectionInvocationId;
382
- }>;
383
- interface RunCurrentState {
384
- outputsByNode: Record<NodeId, NodeOutputs>;
385
- nodeSnapshotsByNodeId: Record<NodeId, NodeExecutionSnapshot>;
386
- /** Append-only history of connection-scoped invocations (LLM/tool) for inspector and canvas. */
387
- connectionInvocations?: ReadonlyArray<ConnectionInvocationRecord>;
388
- mutableState?: PersistedMutableRunState;
389
- }
390
- interface CurrentStateExecutionRequest {
391
- workflow: WorkflowDefinition;
392
- items?: Items;
393
- parent?: ParentExecutionRef;
394
- executionOptions?: RunExecutionOptions;
395
- workflowSnapshot?: PersistedWorkflowSnapshot;
396
- mutableState?: PersistedMutableRunState;
397
- currentState?: RunCurrentState;
398
- stopCondition?: RunStopCondition;
399
- reset?: RunStateResetRequest;
400
- }
401
- type RunStatus = "running" | "pending" | "completed" | "failed";
402
- interface RunSummary {
403
- runId: RunId;
404
- workflowId: WorkflowId;
405
- startedAt: string;
406
- status: RunStatus;
431
+ type CredentialJsonRecord = Readonly<Record<string, unknown>>;
432
+ /**
433
+ * Persisted credential instance with typed `publicConfig`.
434
+ * Hosts may specialize `secretRef` with a stricter union while remaining
435
+ * assignable here for session/test callbacks.
436
+ */
437
+ type CredentialInstanceRecord<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord> = Readonly<{
438
+ instanceId: CredentialInstanceId;
439
+ typeId: CredentialTypeId;
440
+ displayName: string;
441
+ sourceKind: CredentialMaterialSourceKind;
442
+ publicConfig: TPublicConfig;
443
+ secretRef: CredentialJsonRecord;
444
+ tags: ReadonlyArray<string>;
445
+ setupStatus: CredentialSetupStatus;
446
+ createdAt: string;
447
+ updatedAt: string;
407
448
  /**
408
- * Test-case status for runs dispatched as part of a TestSuiteRun. Carries the
409
- * assertion-rollup-corrected outcome the test orchestrator persists onto the row, so the
410
- * executions list can show "failed" for a run whose workflow completed cleanly but whose
411
- * assertions caught regressions. Absent for non-test runs and legacy rows.
449
+ * Pointer to where the credential material bytes live. For OSS / standalone
450
+ * rows this is `{source: "local", ref: instanceId}` and the bytes co-locate
451
+ * with the row in the workspace DB. For managed-mode rows this is
452
+ * `{source: "control-plane", ref: <cp_id>}` and the bytes live at CP.
453
+ *
454
+ * The seam is read through `CredentialMaterialProvider`. See
455
+ * `docs/design/credentials-oauth-unification.md` ("Material provider seam").
412
456
  */
413
- testCaseStatus?: TestCaseRunStatus;
414
- /** ISO timestamp when the run finished (derived from node snapshots or store `updatedAt`); omit while running/pending. */
415
- finishedAt?: string;
416
- parent?: ParentExecutionRef;
417
- executionOptions?: RunExecutionOptions;
418
- }
419
- interface PendingNodeExecution {
420
- runId: RunId;
421
- activationId: NodeActivationId;
422
- workflowId: WorkflowId;
423
- nodeId: NodeId;
424
- itemsIn: number;
425
- inputsByPort: NodeInputsByPort;
426
- receiptId: string;
427
- queue?: string;
428
- batchId?: string;
429
- enqueuedAt: string;
430
- }
431
- interface PersistedRunSchedulingState {
432
- pending?: PendingNodeExecution;
433
- queue: RunQueueEntry[];
434
- }
435
- interface PersistedRunState {
436
- runId: RunId;
437
- workflowId: WorkflowId;
438
- startedAt: string;
439
- /** Canonical terminal time for listings and retention when persisted on the run root. */
440
- finishedAt?: string;
441
- /** Optimistic concurrency / CAS on the run aggregate (repository may increment on save). */
442
- revision?: number;
443
- parent?: ParentExecutionRef;
444
- executionOptions?: RunExecutionOptions;
445
- control?: PersistedRunControlState;
446
- workflowSnapshot?: PersistedWorkflowSnapshot;
447
- mutableState?: PersistedMutableRunState;
448
- /** Frozen at createRun from workflow + runtime defaults for prune/storage decisions. */
449
- policySnapshot?: PersistedRunPolicySnapshot;
450
- /** Successful node completions so far (for activation budget). */
451
- engineCounters?: EngineRunCounters;
452
- status: RunStatus;
453
- pending?: PendingNodeExecution;
454
- queue: RunQueueEntry[];
455
- outputsByNode: Record<NodeId, NodeOutputs>;
456
- nodeSnapshotsByNodeId: Record<NodeId, NodeExecutionSnapshot>;
457
- /** Append-only history of connection invocations (LLM/tool) nested under owning nodes. */
458
- connectionInvocations?: ReadonlyArray<ConnectionInvocationRecord>;
459
- }
460
- interface WorkflowExecutionRepository {
461
- createRun(args: {
462
- runId: RunId;
463
- workflowId: WorkflowId;
464
- startedAt: string;
465
- parent?: ParentExecutionRef;
466
- executionOptions?: RunExecutionOptions;
467
- control?: PersistedRunControlState;
468
- workflowSnapshot?: PersistedWorkflowSnapshot;
469
- mutableState?: PersistedMutableRunState;
470
- policySnapshot?: PersistedRunPolicySnapshot;
471
- engineCounters?: EngineRunCounters;
472
- }): Promise<void>;
473
- load(runId: RunId): Promise<PersistedRunState | undefined>;
474
- loadSchedulingState(runId: RunId): Promise<PersistedRunSchedulingState | undefined>;
475
- save(state: PersistedRunState): Promise<void>;
476
- deleteRun?(runId: RunId): Promise<void>;
477
- }
478
- /** Runs eligible for retention-based pruning (completed or failed, older than cutoff). */
479
- interface RunPruneCandidate {
480
- readonly runId: RunId;
481
- readonly workflowId: WorkflowId;
482
- readonly startedAt: string;
483
- readonly finishedAt: string;
484
- }
485
- type RunResult = {
486
- runId: RunId;
487
- workflowId: WorkflowId;
488
- startedAt: string;
489
- status: "completed";
490
- outputs: Items;
491
- } | {
492
- runId: RunId;
493
- workflowId: WorkflowId;
494
- startedAt: string;
495
- status: "pending";
496
- pending: PendingNodeExecution;
497
- } | {
498
- runId: RunId;
499
- workflowId: WorkflowId;
500
- startedAt: string;
501
- status: "failed";
502
- error: {
503
- message: string;
504
- };
505
- };
506
- type WebhookRunResult = Readonly<{
507
- runId: RunId;
508
- workflowId: WorkflowId;
509
- startedAt: string;
510
- runStatus: "pending" | "completed";
511
- response: Items;
457
+ material: Readonly<{
458
+ source: "local" | "control-plane";
459
+ ref: string;
460
+ }>;
512
461
  }>;
513
- interface PersistedWorkflowTokenRegistryLike {
514
- register(type: TypeToken<unknown>, packageId: string, persistedNameOverride?: string): string;
515
- getTokenId(type: TypeToken<unknown>): string | undefined;
516
- resolve(tokenId: string): TypeToken<unknown> | undefined;
517
- registerFromWorkflows?(workflows: ReadonlyArray<WorkflowDefinition>): void;
518
- }
519
- //#endregion
520
- //#region ../core/src/contracts/retryPolicySpec.types.d.ts
521
462
  /**
522
- * In-process retry policy for runnable nodes. Serialized configs use the same
523
- * `kind` discriminator (`JSON.stringify` / persisted workflows).
524
- *
525
- * `maxAttempts` is the total number of tries including the first (e.g. 3 means up to 2 delays after failures).
463
+ * Arguments passed to `CredentialType.createSession` and `CredentialType.test`.
464
+ * Declare `TPublicConfig` / `TMaterial` on `CredentialType` so implementations are checked
465
+ * against your credential shapes (similar to `NodeExecutionContext.config` for nodes).
526
466
  */
527
- type RetryPolicySpec = NoneRetryPolicySpec | FixedRetryPolicySpec | ExponentialRetryPolicySpec;
528
- interface NoneRetryPolicySpec {
529
- readonly kind: "none";
530
- }
531
- interface FixedRetryPolicySpec {
532
- readonly kind: "fixed";
533
- /** Total attempts including the first execution. Must be >= 1. */
534
- readonly maxAttempts: number;
535
- readonly delayMs: number;
467
+ type CredentialSessionFactoryArgs<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord> = Readonly<{
468
+ instance: CredentialInstanceRecord<TPublicConfig>;
469
+ material: TMaterial;
470
+ publicConfig: TPublicConfig;
471
+ }>;
472
+ type CredentialSessionFactory<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord, TSession = unknown> = (args: CredentialSessionFactoryArgs<TPublicConfig, TMaterial>) => Promise<TSession>;
473
+ type CredentialHealthTester<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord> = (args: CredentialSessionFactoryArgs<TPublicConfig, TMaterial>) => Promise<CredentialHealth>;
474
+ /**
475
+ * Full credential type implementation: `definition` (UI/schema), `createSession`, and `test`.
476
+ * Use this at registration and config boundaries; `CredentialTypeDefinition` is only the schema slice.
477
+ */
478
+ type CredentialType<TPublicConfig extends CredentialJsonRecord = CredentialJsonRecord, TMaterial extends CredentialJsonRecord = CredentialJsonRecord, TSession = unknown> = Readonly<{
479
+ definition: CredentialTypeDefinition;
480
+ createSession: CredentialSessionFactory<TPublicConfig, TMaterial, TSession>;
481
+ test: CredentialHealthTester<TPublicConfig, TMaterial>;
482
+ }>;
483
+ /**
484
+ * Credential type with unspecified generics — used for `CodemationConfig.credentialTypes`, the host registry,
485
+ * and anywhere a concrete `CredentialType<YourPublic, YourMaterial, YourSession>` is placed in a heterogeneous list.
486
+ * Using `any` here avoids unsafe `as` casts while keeping typed `satisfies CredentialType<…>` definitions.
487
+ */
488
+ type AnyCredentialType = CredentialType<any, any, unknown>;
489
+ interface CredentialSessionService {
490
+ getSession<TSession = unknown>(args: Readonly<{
491
+ workflowId: WorkflowId;
492
+ nodeId: NodeId;
493
+ slotKey: string;
494
+ }>): Promise<TSession>;
536
495
  }
537
- interface ExponentialRetryPolicySpec {
538
- readonly kind: "exponential";
539
- /** Total attempts including the first execution. Must be >= 1. */
540
- readonly maxAttempts: number;
541
- readonly initialDelayMs: number;
542
- readonly multiplier: number;
543
- readonly maxDelayMs?: number;
544
- /** When true, each delay is multiplied by a random factor in [1, 1.2). */
545
- readonly jitter?: boolean;
496
+ interface CredentialTypeRegistry {
497
+ listTypes(): ReadonlyArray<CredentialTypeDefinition>;
498
+ getType(typeId: CredentialTypeId): CredentialTypeDefinition | undefined;
546
499
  }
547
500
  //#endregion
548
- //#region ../core/src/contracts/workflowTypes.d.ts
549
- type NodeIdRef<TJson = unknown> = NodeId & Readonly<{
550
- __codemationNodeJson?: TJson;
551
- }>;
552
- type NodeKind = "trigger" | "node";
553
- type JsonPrimitive = string | number | boolean | null;
554
- interface JsonObject {
555
- readonly [key: string]: JsonValue;
556
- }
557
- type JsonValue = JsonPrimitive | JsonObject | JsonArray;
558
- type JsonArray = ReadonlyArray<JsonValue>;
559
- interface Edge {
560
- from: {
561
- nodeId: NodeId;
562
- output: OutputPortKey;
563
- };
564
- to: {
565
- nodeId: NodeId;
566
- input: InputPortKey;
567
- };
501
+ //#region ../core/src/contracts/mcpTypes.d.ts
502
+ type McpServerTransport = "http";
503
+ interface McpServerDeclaration {
504
+ /** Globally unique slug, e.g. "gmail". Workflow authors reference this. */
505
+ id: string;
506
+ displayName: string;
507
+ description: string;
508
+ transport: McpServerTransport;
509
+ url: string;
510
+ /**
511
+ * Credential types accepted by this MCP server, matching CredentialRequirement.acceptedTypes.
512
+ * Absent or empty means no credential is required.
513
+ */
514
+ acceptedCredentialTypes?: ReadonlyArray<string>;
515
+ /**
516
+ * Documentation only in MVP. The bind-time validator checks
517
+ * requiredScopes ⊆ CredentialInstance.scopesGranted.
518
+ */
519
+ requiredScopes?: string[];
520
+ /** Non-secret static headers merged onto every MCP request. */
521
+ staticHeaders?: Record<string, string>;
522
+ /**
523
+ * Overrides for tool descriptions advertised by the MCP server.
524
+ * Applied by the connection pool after tools/list.
525
+ * Key: exact tool name as returned by the server.
526
+ */
527
+ toolDescriptionOverrides?: Record<string, string>;
568
528
  }
529
+ //#endregion
530
+ //#region ../core/src/contracts/collectionTypes.d.ts
569
531
  /**
570
- * Named connection from a parent node to child nodes that exist in {@link WorkflowDefinition.nodes}
571
- * but are not traversed by the main execution graph. Parents are commonly executable nodes, but may
572
- * also be connection-owned nodes for recursive agent attachments.
532
+ * Represents a typed store for a single collection.
533
+ * All rows include auto-managed id, created_at, and updated_at fields.
573
534
  */
574
- interface WorkflowNodeConnection {
575
- readonly parentNodeId: NodeId;
576
- readonly connectionName: NodeConnectionName;
577
- readonly childNodeIds: ReadonlyArray<NodeId>;
578
- }
579
- interface WorkflowDefinition {
580
- id: WorkflowId;
581
- name: string;
582
- nodes: NodeDefinition[];
583
- edges: Edge[];
535
+ interface CollectionStore<TRow extends Record<string, unknown> = Record<string, unknown>> {
584
536
  /**
585
- * Optional metadata: which nodes are connection-owned children (e.g. AI agent `llm` / `tools` slots).
586
- * When omitted, all nodes in {@link nodes} are treated as executable for topology.
537
+ * Insert a new row. id, created_at, and updated_at are auto-populated.
587
538
  */
588
- readonly connections?: ReadonlyArray<WorkflowNodeConnection>;
589
- /** Directory + file-stem path under a workflow discovery root (for UI grouping only). */
590
- discoveryPathSegments?: readonly string[];
591
- /** Retention for run JSON and binaries (seconds). Host/env may supply defaults when omitted. */
592
- readonly prunePolicy?: WorkflowPrunePolicySpec;
593
- /** Whether to keep run data after completion. Host/env may supply defaults when omitted. */
594
- readonly storagePolicy?: WorkflowStoragePolicySpec;
595
- /** Invoked after a node fails permanently (retries exhausted) and node error handler did not recover. */
596
- readonly workflowErrorHandler?: WorkflowErrorHandlerSpec;
597
- }
598
- interface NodeConfigBase {
599
- readonly kind: NodeKind;
600
- readonly type: TypeToken<unknown>;
601
- readonly name?: string;
602
- readonly id?: NodeId;
603
- readonly icon?: string;
604
- readonly execution?: Readonly<{
605
- hint?: "local" | "worker";
606
- queue?: string;
539
+ insert(row: TRow): Promise<TRow & {
540
+ id: string;
541
+ created_at: Date;
542
+ updated_at: Date;
607
543
  }>;
608
- /** In-process execute retries (runnable nodes). Triggers typically omit this. */
609
- readonly retryPolicy?: RetryPolicySpec;
610
- /** Recover from execute failures; return outputs to continue, or rethrow to fail the node. */
611
- readonly nodeErrorHandler?: NodeErrorHandlerSpec;
612
544
  /**
613
- * When true, edges carrying zero items on an output port still schedule single-input downstream nodes.
614
- * Decided from the **source** node that produced the (empty) output. Default (false/undefined): empty
615
- * main batches skip downstream execution and propagate the empty path.
545
+ * Get a single row by id.
616
546
  */
617
- readonly continueWhenEmptyOutput?: boolean;
547
+ get(id: string): Promise<(TRow & {
548
+ id: string;
549
+ created_at: Date;
550
+ updated_at: Date;
551
+ }) | null>;
618
552
  /**
619
- * Declared I/O port names for canvas authoring (unioned with ports inferred from edges).
620
- * Use for dynamic routers (Switch) and future error ports.
553
+ * Find a single row matching the provided filter.
621
554
  */
622
- readonly declaredOutputPorts?: ReadonlyArray<OutputPortKey>;
623
- readonly declaredInputPorts?: ReadonlyArray<InputPortKey>;
624
- getCredentialRequirements?(): ReadonlyArray<CredentialRequirement>;
555
+ findOne(filter: Partial<TRow>): Promise<(TRow & {
556
+ id: string;
557
+ created_at: Date;
558
+ updated_at: Date;
559
+ }) | null>;
625
560
  /**
626
- * Marker: this node emits {@link import("./assertionTypes").AssertionResult}-shaped items on its
627
- * `main` port. The TestSuiteOrchestrator (and host-side TestAssertionPersister) listen for
628
- * `nodeCompleted` events from nodes with this flag set, and persist their output items as
629
- * TestAssertion records (only when the run carries a `testContext`). Set on assertion node
630
- * configs (e.g. `AssertionNodeConfig`, `StringEqualsAssertionNodeConfig`).
561
+ * List rows with optional pagination and filtering.
631
562
  */
632
- readonly emitsAssertions?: true;
563
+ list(opts?: {
564
+ limit?: number;
565
+ offset?: number;
566
+ where?: Partial<TRow>;
567
+ }): Promise<{
568
+ rows: ReadonlyArray<TRow & {
569
+ id: string;
570
+ created_at: Date;
571
+ updated_at: Date;
572
+ }>;
573
+ total: number;
574
+ }>;
633
575
  /**
634
- * Static configuration summary surfaced in the workflow inspector — the design-time
635
- * "what does this node do" panel that renders before any run telemetry exists.
636
- *
637
- * Return 2–6 short label/value pairs derived from this config (method + url for an HTTP
638
- * call, model + tool list for an agent, schedule + timezone for a cron trigger, etc.).
639
- * Values are truncated by the UI; aim for one line each. Return `undefined` to opt out
640
- * — the inspector hides the section when no rows are produced.
641
- *
642
- * Implement on the config class instance so the function can read sibling config fields.
643
- * `defineNode({ inspectorSummary })` plumbs through to this.
576
+ * Update a row by id with partial data.
644
577
  */
645
- inspectorSummary?(): ReadonlyArray<NodeInspectorSummaryRow> | undefined;
578
+ update(id: string, patch: Partial<TRow>): Promise<TRow & {
579
+ id: string;
580
+ created_at: Date;
581
+ updated_at: Date;
582
+ }>;
583
+ /**
584
+ * Delete a row by id. Hard delete only (no soft delete).
585
+ */
586
+ delete(id: string): Promise<{
587
+ deleted: boolean;
588
+ }>;
646
589
  }
647
590
  /**
648
- * One row of a node's static configuration summary. See {@link NodeConfigBase.inspectorSummary}.
591
+ * Runtime collections context: keyed by collection name.
649
592
  */
650
- interface NodeInspectorSummaryRow {
651
- readonly label: string;
652
- readonly value: string;
593
+ type CollectionsContext = Readonly<Record<string, CollectionStore>>;
594
+ //#endregion
595
+ //#region ../core/src/contracts/workflowActivationPolicy.d.ts
596
+ /**
597
+ * Host-controlled policy: when false, trigger {@link TriggerNode} setup is skipped and webhook routes
598
+ * for that workflow are not registered (see engine trigger runtime + webhook matcher).
599
+ */
600
+ interface WorkflowActivationPolicy {
601
+ isActive(workflowId: WorkflowId): boolean;
653
602
  }
654
- declare const runnableNodeInputType: unique symbol;
655
- declare const runnableNodeOutputType: unique symbol;
656
- declare const triggerNodeOutputType: unique symbol;
603
+ //#endregion
604
+ //#region ../core/src/authoring/defineNode.types.d.ts
605
+ type ResolvableCredentialType = AnyCredentialType | CredentialTypeId;
606
+ type DefinedNodeCredentialBinding = ResolvableCredentialType | Readonly<{
607
+ readonly type: ResolvableCredentialType | ReadonlyArray<ResolvableCredentialType>;
608
+ readonly label?: string;
609
+ readonly optional?: true;
610
+ readonly helpText?: string;
611
+ readonly helpUrl?: string;
612
+ }>;
613
+ type DefinedNodeCredentialBindings = Readonly<Record<string, DefinedNodeCredentialBinding>>;
614
+ type DefinedNodeConfigInput<TConfigResolved extends CredentialJsonRecord, TItemJson> = ParamDeep<TConfigResolved, TItemJson>;
615
+ interface DefinedNode<TKey$1 extends string, TConfig extends CredentialJsonRecord, TInputJson$1, TOutputJson$1, _TBindings extends DefinedNodeCredentialBindings | undefined = undefined> {
616
+ readonly kind: "defined-node";
617
+ readonly key: TKey$1;
618
+ readonly title: string;
619
+ readonly description?: string;
620
+ create<TConfigItemJson = TInputJson$1>(config: DefinedNodeConfigInput<TConfig, TConfigItemJson>, name?: string, id?: string): RunnableNodeConfig<TInputJson$1, TOutputJson$1>;
621
+ register(context: {
622
+ registerNode<TValue>(token: TypeToken<TValue>, implementation?: TypeToken<TValue>): void;
623
+ }): void;
624
+ }
625
+ //#endregion
626
+ //#region ../core/src/authoring/defineHumanApprovalNode.types.d.ts
657
627
  /**
658
- * Runnable node: **`TInputJson`** is what **`inputSchema`** validates on **`item.json`** (the wire payload).
659
- * **`TOutputJson`** is emitted `item.json` on outputs.
628
+ * Decision shape merged into `item.json` after a HITL approval task resolves.
629
+ *
630
+ * - `"approved"` / `"rejected"` — from a human decision (uses `approvedPredicate`).
631
+ * - `"timed-out"` — timeout fired with `onTimeout: "halt"`.
632
+ * - `"auto-accepted"` — timeout fired with `onTimeout: "auto-accept"`.
660
633
  */
661
- interface RunnableNodeConfig<TInputJson$1 = unknown, TOutputJson$1 = unknown> extends NodeConfigBase {
662
- readonly kind: "node";
663
- readonly [runnableNodeInputType]?: TInputJson$1;
664
- readonly [runnableNodeOutputType]?: TOutputJson$1;
634
+ interface HumanApprovalDecisionResult {
635
+ readonly status: "approved" | "rejected" | "timed-out" | "auto-accepted";
636
+ /** Identity of the person who decided; absent for automated outcomes. */
637
+ readonly actor?: HumanTaskActor;
638
+ /** ISO 8601 timestamp of the decision. */
639
+ readonly decidedAt?: Date;
640
+ /** Optional free-text note from the reviewer. */
641
+ readonly note?: string;
665
642
  /**
666
- * Optional Zod input contract for {@link RunnableNode} when not set on the node class.
667
- * Resolution order: node instance `inputSchema`, then config `inputSchema`, then `z.unknown()`.
643
+ * Full raw decision payload (only present for `"approved"` / `"rejected"`).
644
+ * Shape is determined by the channel's `decisionSchema`.
668
645
  */
669
- readonly inputSchema?: ZodType<TInputJson$1>;
646
+ readonly payload?: Record<string, unknown>;
647
+ }
648
+ /**
649
+ * Output item shape emitted by a `defineHumanApprovalNode`-based node.
650
+ * Original `item.json` fields are preserved and `decision` is merged in.
651
+ * If the input `item.json` already contained a `decision` key it is **overwritten**.
652
+ */
653
+ type HumanApprovalOutputJson<TInputJson$1 extends Record<string, unknown>> = TInputJson$1 & {
654
+ readonly decision: HumanApprovalDecisionResult;
655
+ };
656
+ /**
657
+ * Extends {@link DefinedNode} with the `humanApprovalToolBehavior` metadata marker.
658
+ * Story 10 reads this field when attaching the node as an agent tool.
659
+ */
660
+ interface DefinedHumanApprovalNode<TKey$1 extends string, TConfig extends CredentialJsonRecord, TInputJson$1 extends Record<string, unknown>, TBindings extends DefinedNodeCredentialBindings | undefined = undefined> extends DefinedNode<TKey$1, TConfig, TInputJson$1, HumanApprovalOutputJson<TInputJson$1>, TBindings> {
670
661
  /**
671
- * When an activation receives **zero** input items, the engine normally runs `execute` zero times.
672
- * Set to **`runOnce`** to run `execute` once with an empty `items` batch (and a synthetic wire item for schema parsing).
673
- * Used by batch-style callback nodes (built-in `Callback`) so `callback([], ctx)` still runs.
662
+ * Behavior hint consumed by the agent runtime (story 10) when this node is attached as a tool.
663
+ * `"return"` (default) return the rejection to the agent as a tool result.
664
+ * `"halt"` halt the agent run on rejection.
665
+ *
666
+ * Standalone DSL usage ignores this field.
674
667
  */
675
- readonly emptyBatchExecution?: "skip" | "runOnce";
668
+ readonly humanApprovalToolBehavior: {
669
+ onRejected: "return" | "halt";
670
+ };
676
671
  }
677
- declare const triggerNodeSetupStateType: unique symbol;
678
- interface TriggerNodeConfig<TOutputJson$1 = unknown, TSetupState$1 extends JsonValue | undefined = undefined> extends NodeConfigBase {
679
- readonly kind: "trigger";
680
- readonly [triggerNodeOutputType]?: TOutputJson$1;
681
- readonly [triggerNodeSetupStateType]?: TSetupState$1;
672
+ //#endregion
673
+ //#region ../core/src/workflow/dsl/workflowBuilderTypes.d.ts
674
+ type AnyRunnableNodeConfig = RunnableNodeConfig<any, any>;
675
+ type AnyTriggerNodeConfig = TriggerNodeConfig<any>;
676
+ type ValidStepSequence<TCurrentJson, TSteps extends ReadonlyArray<AnyRunnableNodeConfig>> = TSteps extends readonly [] ? readonly [] : TSteps extends readonly [infer TFirst, ...infer TRest] ? TFirst extends RunnableNodeConfig<TCurrentJson, infer TNextJson> ? TRest extends ReadonlyArray<AnyRunnableNodeConfig> ? readonly [TFirst, ...ValidStepSequence<TNextJson, TRest>] : never : never : TSteps;
677
+ type StepSequenceOutput<TCurrentJson, TSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined> = TSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? TSteps extends readonly [] ? TCurrentJson : TSteps extends readonly [infer TFirst, ...infer TRest] ? TFirst extends RunnableNodeConfig<TCurrentJson, infer TNextJson> ? TRest extends ReadonlyArray<AnyRunnableNodeConfig> ? StepSequenceOutput<TNextJson, TRest> : never : never : TCurrentJson : TCurrentJson;
678
+ type TypesMatch<TLeft, TRight> = [TLeft] extends [TRight] ? ([TRight] extends [TLeft] ? true : false) : false;
679
+ type BranchOutputGuard<TCurrentJson, TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined, TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined> = TypesMatch<StepSequenceOutput<TCurrentJson, TTrueSteps>, StepSequenceOutput<TCurrentJson, TFalseSteps>> extends true ? unknown : never;
680
+ type BranchStepsArg<TCurrentJson, TSteps extends ReadonlyArray<AnyRunnableNodeConfig>> = TSteps & ValidStepSequence<TCurrentJson, TSteps>;
681
+ type BranchMoreArgs<TCurrentJson, TFirstStep extends RunnableNodeConfig<TCurrentJson, any>, TRestSteps extends ReadonlyArray<AnyRunnableNodeConfig>> = TRestSteps & ValidStepSequence<RunnableNodeOutputJson<TFirstStep>, TRestSteps>;
682
+ type BooleanWhenOverloads<TCurrentJson, TReturn> = {
683
+ <TSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(branch: boolean, steps: BranchStepsArg<TCurrentJson, TSteps>): TReturn;
684
+ <TFirstStep extends RunnableNodeConfig<TCurrentJson, any>, TRestSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(branch: boolean, step: TFirstStep, ...more: BranchMoreArgs<TCurrentJson, TFirstStep, TRestSteps>): TReturn;
685
+ };
686
+ //#endregion
687
+ //#region ../core/src/workflow/dsl/WhenBuilder.d.ts
688
+ declare class WhenBuilder<TCurrentJson> {
689
+ private readonly wf;
690
+ private readonly from;
691
+ private readonly branchPort;
692
+ constructor(wf: WorkflowBuilder, from: NodeRef, branchPort: OutputPortKey);
693
+ addBranch<TSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(steps: TSteps & ValidStepSequence<TCurrentJson, TSteps>): this;
694
+ readonly when: BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>>;
695
+ build(): WorkflowDefinition;
696
+ }
697
+ //#endregion
698
+ //#region ../core/src/workflow/dsl/ChainCursorResolver.d.ts
699
+ type ChainCursorEndpoint = Readonly<{
700
+ node: NodeRef;
701
+ output: OutputPortKey;
702
+ inputPortHint?: InputPortKey;
703
+ }>;
704
+ type ChainCursorWhenOverloads<TCurrentJson> = BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>> & {
705
+ <TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined, TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined>(branches: Readonly<{
706
+ true?: TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? BranchStepsArg<TCurrentJson, TTrueSteps> : never;
707
+ false?: TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? BranchStepsArg<TCurrentJson, TFalseSteps> : never;
708
+ }> & BranchOutputGuard<TCurrentJson, TTrueSteps, TFalseSteps>): ChainCursor<StepSequenceOutput<TCurrentJson, TTrueSteps>>;
709
+ };
710
+ declare class ChainCursor<TCurrentJson> {
711
+ private readonly wf;
712
+ private readonly endpoints;
713
+ constructor(wf: WorkflowBuilder, endpoints: ReadonlyArray<ChainCursorEndpoint>);
714
+ then<TOutputJson$1, TConfig extends RunnableNodeConfig<TCurrentJson, TOutputJson$1>>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>>;
715
+ thenIntoInputHints<TOutputJson$1, TConfig extends RunnableNodeConfig<any, TOutputJson$1>>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>>;
716
+ readonly when: ChainCursorWhenOverloads<TCurrentJson>;
717
+ route<TNextJson$1>(branches: Readonly<Record<OutputPortKey, (branch: ChainCursor<TCurrentJson>) => ChainCursor<TNextJson$1> | undefined>>): ChainCursor<TNextJson$1>;
682
718
  /**
683
- * Distinguishes triggers driven by the live activation policy (webhooks, cron, polling) from
684
- * triggers driven only by the {@link TestSuiteOrchestrator}. `WorkflowActivation` skips
685
- * `"test"` triggers; the orchestrator skips `"live"` triggers. Defaults to `"live"` when omitted.
719
+ * Chainable shorthand for `.then(node.create(config, metadata?.name, metadata?.nodeId))`.
720
+ *
721
+ * Signals to readers that this step suspends the run and waits for a human decision.
722
+ * Throws at workflow-build time if `node` was not created via `defineHumanApprovalNode`.
723
+ *
724
+ * @example
725
+ * ```ts
726
+ * workflow
727
+ * .trigger(...)
728
+ * .humanApproval(inboxApproval, { title: "Approve?", body: "...", priority: "normal" })
729
+ * .then(nextStep.create(...))
730
+ * .build();
731
+ * ```
686
732
  */
687
- readonly triggerKind?: "live" | "test";
688
- }
689
- type RunnableNodeOutputJson<TConfig extends RunnableNodeConfig<any, any>> = TConfig extends RunnableNodeConfig<any, infer TOutputJson> ? TOutputJson : never;
690
- type TriggerNodeOutputJson<TConfig extends TriggerNodeConfig<any, any>> = TConfig extends TriggerNodeConfig<infer TOutputJson, any> ? TOutputJson : never;
691
- interface NodeDefinition {
692
- id: NodeId;
693
- kind: NodeKind;
694
- type: TypeToken<unknown>;
695
- name?: string;
696
- config: NodeConfigBase;
733
+ humanApproval<TKey$1 extends string, TConfig extends Record<string, unknown>, TBindings extends DefinedNodeCredentialBindings | undefined = undefined>(node: DefinedHumanApprovalNode<TKey$1, TConfig, TCurrentJson & Record<string, unknown>, TBindings>, config: TConfig, metadata?: {
734
+ name?: string;
735
+ nodeId?: string;
736
+ }): ChainCursor<HumanApprovalOutputJson<TCurrentJson & Record<string, unknown>>>;
737
+ build(): WorkflowDefinition;
738
+ private resolveSharedInputPortHint;
697
739
  }
698
- interface NodeRef {
699
- id: NodeId;
700
- kind: NodeKind;
701
- name?: string;
740
+ //#endregion
741
+ //#region ../core/src/workflow/dsl/WorkflowBuilder.d.ts
742
+ declare class WorkflowBuilder {
743
+ private readonly meta;
744
+ private readonly options?;
745
+ private readonly nodes;
746
+ private readonly edges;
747
+ constructor(meta: {
748
+ id: WorkflowId;
749
+ name: string;
750
+ }, options?: Readonly<Record<string, never>> | undefined);
751
+ private add;
752
+ private connect;
753
+ trigger<TConfig extends AnyTriggerNodeConfig>(config: TConfig): ChainCursor<TriggerNodeOutputJson<TConfig>>;
754
+ start<TConfig extends AnyRunnableNodeConfig>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>>;
755
+ build(): WorkflowDefinition;
756
+ private validateNodeIds;
702
757
  }
703
- type PairedItemRef = Readonly<{
704
- nodeId: NodeId;
705
- output: OutputPortKey;
706
- itemIndex: number;
707
- }>;
708
- type BinaryPreviewKind = "image" | "audio" | "video" | "download";
709
- type BinaryAttachment = Readonly<{
710
- id: string;
711
- storageKey: string;
712
- mimeType: string;
713
- size: number;
714
- storageDriver: string;
715
- previewKind: BinaryPreviewKind;
716
- createdAt: string;
717
- runId: RunId;
718
- workflowId: WorkflowId;
719
- nodeId: NodeId;
720
- activationId: NodeActivationId;
721
- filename?: string;
722
- sha256?: string;
723
- }>;
724
- type ItemBinary = Readonly<Record<string, BinaryAttachment>>;
725
- type Item<TJson = unknown> = Readonly<{
726
- json: TJson;
727
- binary?: ItemBinary;
728
- meta?: Readonly<Record<string, unknown>>;
729
- paired?: ReadonlyArray<PairedItemRef>;
730
- }>;
731
- type Items<TJson = unknown> = ReadonlyArray<Item<TJson>>;
732
- type NodeOutputs = Partial<Record<OutputPortKey, Items>>;
733
- type RunId = string;
734
- type NodeActivationId = string;
758
+ //#endregion
759
+ //#region ../core/src/events/runEvents.d.ts
735
760
  /**
736
- * One per-item iteration of a runnable node's execute loop. Refines `NodeActivationId` for
737
- * per-item connection invocations and telemetry. Undefined when the executing node is a batch
738
- * node or trigger that does not iterate items.
761
+ * Outcome of a single test case (one workflow run dispatched by the test-suite orchestrator).
762
+ * - `running`: workflow still in flight
763
+ * - `succeeded`: workflow completed AND all assertions passed (or no assertions)
764
+ * - `failed`: workflow failed OR (workflow completed but ≥1 assertion failed)
765
+ * - `errored` / `cancelled`: workflow itself errored or was cancelled
739
766
  */
740
- type NodeIterationId = string;
741
- interface ParentExecutionRef {
767
+ type TestCaseRunStatus = "running" | "succeeded" | "failed" | "errored" | "cancelled";
768
+ /** Aggregate outcome of a TestSuiteRun. */
769
+ type TestSuiteRunStatus = "succeeded" | "failed" | "partial" | "errored" | "cancelled";
770
+ type RunEvent = Readonly<{
771
+ kind: "runCreated";
742
772
  runId: RunId;
743
773
  workflowId: WorkflowId;
744
- nodeId: NodeId;
745
- /** Subworkflow depth of the **spawning** run (0 = root). Passed when starting a child run. */
746
- subworkflowDepth?: number;
747
- /** Effective max node activations from the parent run (propagated to child policy merge). */
748
- engineMaxNodeActivations?: number;
749
- /** Effective max subworkflow depth from the parent run (propagated to child policy merge). */
750
- engineMaxSubworkflowDepth?: number;
751
- /**
752
- * Test-suite linkage inherited by the child subworkflow run. Set by whichever node
753
- * spawns the subworkflow when its own `ctx.testContext` is present, so assertions
754
- * emitted inside a subworkflow land under the correct parent test case.
755
- */
756
- testContext?: RunTestContext;
757
- }
758
- interface RunDataSnapshot {
759
- getOutputs(nodeId: NodeId): NodeOutputs | undefined;
760
- getOutputItems<TJson = unknown>(nodeId: NodeId | NodeIdRef<TJson>, output?: OutputPortKey): Items<TJson>;
761
- getOutputItem<TJson = unknown>(nodeId: NodeId | NodeIdRef<TJson>, itemIndex: number, output?: OutputPortKey): Item<TJson> | undefined;
762
- }
763
- interface RunIdFactory {
764
- makeRunId(): RunId;
765
- }
766
- interface ActivationIdFactory {
767
- makeActivationId(): NodeActivationId;
768
- }
769
- /** Whether to persist run execution data after the workflow finishes. */
770
- type WorkflowStoragePolicyMode = "ALL" | "SUCCESS" | "ERROR" | "NEVER";
771
- type WorkflowStoragePolicySpec = WorkflowStoragePolicyMode | TypeToken<WorkflowStoragePolicyResolver>;
772
- interface WorkflowStoragePolicyResolver {
773
- shouldPersist(args: WorkflowStoragePolicyDecisionArgs): boolean | Promise<boolean>;
774
- }
775
- interface WorkflowStoragePolicyDecisionArgs {
776
- readonly runId: RunId;
777
- readonly workflowId: WorkflowId;
778
- readonly workflow: WorkflowDefinition;
779
- readonly finalStatus: "completed" | "failed";
780
- readonly startedAt: string;
781
- readonly finishedAt: string;
782
- }
783
- interface WorkflowPrunePolicySpec {
784
- readonly runDataRetentionSeconds?: number;
785
- readonly binaryRetentionSeconds?: number;
786
- readonly telemetrySpanRetentionSeconds?: number;
787
- readonly telemetryArtifactRetentionSeconds?: number;
788
- readonly telemetryMetricRetentionSeconds?: number;
774
+ parent?: ParentExecutionRef;
775
+ at: string;
776
+ }> | Readonly<{
777
+ kind: "runSaved";
778
+ runId: RunId;
779
+ workflowId: WorkflowId;
780
+ parent?: ParentExecutionRef;
781
+ at: string;
782
+ state: PersistedRunState;
783
+ }> | Readonly<{
784
+ kind: "nodeQueued";
785
+ runId: RunId;
786
+ workflowId: WorkflowId;
787
+ parent?: ParentExecutionRef;
788
+ at: string;
789
+ snapshot: NodeExecutionSnapshot;
790
+ }> | Readonly<{
791
+ kind: "nodeStarted";
792
+ runId: RunId;
793
+ workflowId: WorkflowId;
794
+ parent?: ParentExecutionRef;
795
+ at: string;
796
+ snapshot: NodeExecutionSnapshot;
797
+ }> | Readonly<{
798
+ kind: "nodeCompleted";
799
+ runId: RunId;
800
+ workflowId: WorkflowId;
801
+ parent?: ParentExecutionRef;
802
+ at: string;
803
+ snapshot: NodeExecutionSnapshot;
804
+ }> | Readonly<{
805
+ kind: "nodeFailed";
806
+ runId: RunId;
807
+ workflowId: WorkflowId;
808
+ parent?: ParentExecutionRef;
809
+ at: string;
810
+ snapshot: NodeExecutionSnapshot;
811
+ }> | Readonly<{
812
+ kind: "connectionInvocationStarted";
813
+ runId: RunId;
814
+ workflowId: WorkflowId;
815
+ parent?: ParentExecutionRef;
816
+ at: string;
817
+ record: ConnectionInvocationRecord;
818
+ }> | Readonly<{
819
+ kind: "connectionInvocationCompleted";
820
+ runId: RunId;
821
+ workflowId: WorkflowId;
822
+ parent?: ParentExecutionRef;
823
+ at: string;
824
+ record: ConnectionInvocationRecord;
825
+ }> | Readonly<{
826
+ kind: "connectionInvocationFailed";
827
+ runId: RunId;
828
+ workflowId: WorkflowId;
829
+ parent?: ParentExecutionRef;
830
+ at: string;
831
+ record: ConnectionInvocationRecord;
832
+ }> | Readonly<{
833
+ kind: "testSuiteStarted";
834
+ testSuiteRunId: TestSuiteRunId;
835
+ workflowId: WorkflowId;
836
+ triggerNodeId: string;
837
+ triggerNodeName?: string;
838
+ concurrency: number;
839
+ at: string;
840
+ }> | Readonly<{
841
+ kind: "testSuiteFinished";
842
+ testSuiteRunId: TestSuiteRunId;
843
+ workflowId: WorkflowId;
844
+ status: TestSuiteRunStatus;
845
+ totalCases: number;
846
+ passedCases: number;
847
+ failedCases: number;
848
+ at: string;
849
+ }> | Readonly<{
850
+ kind: "testCaseStarted";
851
+ testSuiteRunId: TestSuiteRunId;
852
+ testCaseIndex: number;
853
+ runId: RunId;
854
+ workflowId: WorkflowId;
855
+ testCaseLabel?: string;
856
+ at: string;
857
+ }> | Readonly<{
858
+ kind: "testCaseCompleted";
859
+ testSuiteRunId: TestSuiteRunId;
860
+ testCaseIndex: number;
861
+ runId: RunId;
862
+ workflowId: WorkflowId;
863
+ status: TestCaseRunStatus;
864
+ at: string;
865
+ }>;
866
+ interface RunEventSubscription {
867
+ close(): Promise<void>;
789
868
  }
790
- interface PersistedRunPolicySnapshot {
791
- readonly retentionSeconds?: number;
792
- readonly binaryRetentionSeconds?: number;
793
- readonly telemetrySpanRetentionSeconds?: number;
794
- readonly telemetryArtifactRetentionSeconds?: number;
795
- readonly telemetryMetricRetentionSeconds?: number;
796
- readonly storagePolicy: WorkflowStoragePolicyMode;
869
+ interface RunEventBus {
870
+ publish(event: RunEvent): Promise<void>;
871
+ subscribe(onEvent: (event: RunEvent) => void): Promise<RunEventSubscription>;
872
+ subscribeToWorkflow(workflowId: WorkflowId, onEvent: (event: RunEvent) => void): Promise<RunEventSubscription>;
797
873
  }
798
- interface WorkflowErrorHandler {
799
- onError(ctx: WorkflowErrorContext): void | Promise<void>;
874
+ //#endregion
875
+ //#region ../core/src/policies/executionLimits/EngineExecutionLimitsPolicy.d.ts
876
+ interface EngineExecutionLimitsPolicyConfig {
877
+ readonly defaultMaxNodeActivations: number;
878
+ readonly hardMaxNodeActivations: number;
879
+ readonly defaultMaxSubworkflowDepth: number;
880
+ readonly hardMaxSubworkflowDepth: number;
800
881
  }
801
- interface WorkflowErrorContext {
802
- readonly runId: RunId;
803
- readonly workflowId: WorkflowId;
804
- readonly workflow: WorkflowDefinition;
805
- readonly failedNodeId: NodeId;
806
- readonly error: Error;
807
- readonly startedAt: string;
808
- readonly finishedAt: string;
882
+ //#endregion
883
+ //#region ../core/src/contracts/runTypes.d.ts
884
+ /**
885
+ * Test-suite linkage for a run. When set, this run was started by a TestSuiteOrchestrator
886
+ * as one test case inside a TestSuiteRun. The `IsTestRun` node and host-side persisters key
887
+ * off the presence of this field. Subworkflow runs inherit it from their parent run.
888
+ */
889
+ interface RunTestContext {
890
+ readonly testSuiteRunId: string;
891
+ readonly testCaseIndex: number;
892
+ /**
893
+ * Optional human-friendly label for this test case (e.g. an email subject when fixtures
894
+ * are loaded from a mailbox). Resolved per item by `TestTrigger.caseLabel(item)` if set,
895
+ * persisted on `Run.test_case_label` so the Tests-tab tree-table can show "RFQ for batch 14"
896
+ * instead of "run_1777755971399_bbb86beac1396".
897
+ */
898
+ readonly testCaseLabel?: string;
809
899
  }
810
- type WorkflowErrorHandlerSpec = TypeToken<WorkflowErrorHandler> | WorkflowErrorHandler;
811
- interface NodeErrorHandlerArgs<TConfig extends NodeConfigBase = NodeConfigBase> {
812
- readonly kind: "single" | "multi";
813
- readonly items: Items;
814
- readonly inputsByPort: Readonly<Record<InputPortKey, Items>> | undefined;
815
- readonly ctx: NodeExecutionContext<TConfig>;
816
- readonly error: Error;
900
+ interface RunExecutionOptions {
901
+ /** Run-intent override: force the inline scheduler and bypass node-level offload decisions. */
902
+ localOnly?: boolean;
903
+ /** Marks runs started from webhook handling so orchestration can apply webhook-specific continuation rules. */
904
+ webhook?: boolean;
905
+ mode?: "manual" | "debug";
906
+ sourceWorkflowId?: WorkflowId;
907
+ sourceRunId?: RunId;
908
+ derivedFromRunId?: RunId;
909
+ isMutable?: boolean;
910
+ /** Set by the engine for this run: 0 = root, 1 = first child subworkflow, … */
911
+ subworkflowDepth?: number;
912
+ /** Effective cap after engine policy merge (successful node completions per run). */
913
+ maxNodeActivations?: number;
914
+ /** Effective cap after engine policy merge (subworkflow nesting). */
915
+ maxSubworkflowDepth?: number;
916
+ /** Present iff started by a TestSuiteOrchestrator; propagates to subworkflow runs via {@link ParentExecutionRef.testContext}. */
917
+ testContext?: RunTestContext;
817
918
  }
818
- interface NodeErrorHandler {
819
- handle<TConfig extends NodeConfigBase>(args: NodeErrorHandlerArgs<TConfig>): Promise<NodeOutputs>;
919
+ /** Engine-owned counters persisted with the run (worker-safe). */
920
+ interface EngineRunCounters {
921
+ completedNodeActivations: number;
820
922
  }
821
- type NodeErrorHandlerSpec = TypeToken<NodeErrorHandler> | NodeErrorHandler;
822
- //#endregion
823
- //#region ../core/src/contracts/testTriggerTypes.d.ts
824
- /**
825
- * Identifier minted by the host (or in-memory test runner) for one execution of a test suite.
826
- * One TestSuiteRun produces N child workflow runs, one per item yielded by `generateItems`.
827
- */
828
- type TestSuiteRunId = string;
829
- //#endregion
830
- //#region ../core/src/contracts/CostTrackingTelemetryContract.d.ts
831
- type CostTrackingComponent = "chat" | "ocr" | "rag";
832
- interface CostTrackingUsageRecord {
833
- readonly component: CostTrackingComponent;
834
- readonly provider: string;
835
- readonly operation: string;
836
- readonly pricingKey: string;
837
- readonly usageUnit: string;
838
- readonly quantity: number;
839
- readonly modelName?: string;
840
- readonly attributes?: TelemetryAttributes;
923
+ type RunStopCondition = Readonly<{
924
+ kind: "workflowCompleted";
925
+ }> | Readonly<{
926
+ kind: "nodeCompleted";
927
+ nodeId: NodeId;
928
+ }>;
929
+ interface RunStateResetRequest {
930
+ clearFromNodeId: NodeId;
841
931
  }
842
- interface CostTrackingPriceQuote {
843
- readonly currency: string;
844
- readonly currencyScale: number;
845
- readonly estimatedAmountMinor: number;
846
- readonly estimateKind: "catalog";
932
+ interface PersistedRunControlState {
933
+ stopCondition?: RunStopCondition;
847
934
  }
848
- interface CostTrackingTelemetry {
849
- captureUsage(args: CostTrackingUsageRecord): Promise<CostTrackingPriceQuote | undefined>;
850
- forScope(scope: TelemetryScope): CostTrackingTelemetry;
935
+ interface PersistedWorkflowSnapshotNode {
936
+ id: NodeId;
937
+ kind: NodeKind;
938
+ name?: string;
939
+ nodeTokenId: PersistedTokenId;
940
+ configTokenId: PersistedTokenId;
941
+ tokenName?: string;
942
+ configTokenName?: string;
943
+ config: unknown;
944
+ /** Pre-computed static configuration summary; populated by WorkflowSnapshotCodec. */
945
+ inspectorSummary?: ReadonlyArray<Readonly<{
946
+ label: string;
947
+ value: string;
948
+ }>>;
851
949
  }
852
- //#endregion
853
- //#region ../core/src/contracts/telemetryTypes.d.ts
854
- type TelemetryAttributePrimitive = string | number | boolean | null;
855
- interface TelemetryAttributes {
856
- readonly [key: string]: TelemetryAttributePrimitive | undefined;
950
+ interface PersistedWorkflowSnapshot {
951
+ id: WorkflowId;
952
+ name: string;
953
+ nodes: ReadonlyArray<PersistedWorkflowSnapshotNode>;
954
+ edges: ReadonlyArray<Edge>;
955
+ /** When the snapshot was built from a live workflow definition that configured a workflow error handler. */
956
+ workflowErrorHandlerConfigured?: boolean;
957
+ /** Connection metadata for child nodes not in the execution graph (e.g. AI agent attachments). */
958
+ connections?: ReadonlyArray<WorkflowNodeConnection>;
857
959
  }
858
- interface TelemetryMetricRecord {
859
- readonly name: string;
860
- readonly value: number;
861
- readonly unit?: string;
862
- readonly attributes?: TelemetryAttributes;
960
+ type PinnedNodeOutputsByPort = Readonly<Record<OutputPortKey, Items>>;
961
+ interface PersistedMutableNodeState {
962
+ pinnedOutputsByPort?: PinnedNodeOutputsByPort;
963
+ lastDebugInput?: Items;
863
964
  }
864
- interface TelemetrySpanEventRecord {
865
- readonly name: string;
866
- readonly occurredAt?: Date;
867
- readonly attributes?: TelemetryAttributes;
965
+ interface PersistedMutableRunState {
966
+ nodesById: Readonly<Record<NodeId, PersistedMutableNodeState>>;
868
967
  }
869
- interface TelemetryArtifactAttachment {
870
- readonly kind: string;
871
- readonly contentType: string;
872
- readonly previewText?: string;
873
- readonly previewJson?: JsonValue;
874
- readonly payloadText?: string;
875
- readonly payloadJson?: JsonValue;
876
- readonly bytes?: number;
877
- readonly truncated?: boolean;
878
- readonly expiresAt?: Date;
968
+ type NodeInputsByPort = Readonly<Record<InputPortKey, Items>>;
969
+ interface RunQueueEntry {
970
+ nodeId: NodeId;
971
+ input: Items;
972
+ toInput?: InputPortKey;
973
+ batchId?: string;
974
+ from?: Readonly<{
975
+ nodeId: NodeId;
976
+ output: OutputPortKey;
977
+ }>;
978
+ collect?: Readonly<{
979
+ expectedInputs: ReadonlyArray<InputPortKey>;
980
+ received: Readonly<Record<InputPortKey, Items>>;
981
+ }>;
879
982
  }
880
- interface TelemetryArtifactReference {
881
- readonly artifactId: string;
882
- readonly traceId?: string;
883
- readonly spanId?: string;
983
+ type NodeExecutionStatus = "pending" | "queued" | "running" | "completed" | "failed" | "skipped" | "hitl-approved" | "hitl-rejected" | "hitl-timeout" | "hitl-auto-accepted" | "hitl-cancelled";
984
+ interface NodeExecutionError {
985
+ message: string;
986
+ name?: string;
987
+ stack?: string;
988
+ details?: JsonValue;
884
989
  }
885
- interface TelemetrySpanEnd {
886
- readonly status?: "ok" | "error";
887
- readonly statusMessage?: string;
888
- readonly endedAt?: Date;
889
- readonly attributes?: TelemetryAttributes;
990
+ interface NodeExecutionSnapshot {
991
+ runId: RunId;
992
+ workflowId: WorkflowId;
993
+ nodeId: NodeId;
994
+ activationId?: NodeActivationId;
995
+ parent?: ParentExecutionRef;
996
+ status: NodeExecutionStatus;
997
+ usedPinnedOutput?: boolean;
998
+ queuedAt?: string;
999
+ startedAt?: string;
1000
+ finishedAt?: string;
1001
+ updatedAt: string;
1002
+ inputsByPort?: NodeInputsByPort;
1003
+ outputs?: NodeOutputs;
1004
+ error?: NodeExecutionError;
1005
+ /**
1006
+ * When the node is a SubWorkflow invocation, the run id of the child run it spawned.
1007
+ * Populated after the child run completes so the UI can deep-link to that specific execution.
1008
+ */
1009
+ childRunId?: RunId;
890
1010
  }
891
- interface TelemetryChildSpanStart {
892
- readonly name: string;
893
- readonly kind?: "internal" | "client";
894
- readonly startedAt?: Date;
895
- readonly attributes?: TelemetryAttributes;
1011
+ /** Stable id for a single connection invocation row in {@link ConnectionInvocationRecord}. */
1012
+ type ConnectionInvocationId = string;
1013
+ /**
1014
+ * One logical LLM or tool call under an owning workflow node (e.g. AI agent).
1015
+ * The owning node defines what {@link managedInput} and {@link managedOutput} contain.
1016
+ */
1017
+ interface ConnectionInvocationRecord {
1018
+ readonly invocationId: ConnectionInvocationId;
1019
+ readonly runId: RunId;
1020
+ readonly workflowId: WorkflowId;
1021
+ readonly connectionNodeId: NodeId;
1022
+ readonly parentAgentNodeId: NodeId;
1023
+ readonly parentAgentActivationId: NodeActivationId;
1024
+ readonly status: NodeExecutionStatus;
1025
+ readonly managedInput?: JsonValue;
1026
+ readonly managedOutput?: JsonValue;
1027
+ /** Short human-readable description of what this invocation is doing right now (e.g. `"calling search_messages"`). Rendered as a sub-line on the canvas node card. */
1028
+ readonly statusLabel?: string;
1029
+ /** Stable identifier for the thing this invocation acts on (e.g. an MCP tool name like `"search_messages"`). Persists across status transitions so the inspector can show it on completed/failed entries too. Connection nodes that ARE the tool (e.g. node-backed agent tools) leave this unset — the parent node id already identifies the subject. */
1030
+ readonly subjectName?: string;
1031
+ readonly error?: NodeExecutionError;
1032
+ readonly queuedAt?: string;
1033
+ readonly startedAt?: string;
1034
+ readonly finishedAt?: string;
1035
+ readonly updatedAt: string;
1036
+ /** Per-item iteration id minted by the engine when this invocation occurred inside a runnable node's per-item loop. */
1037
+ readonly iterationId?: NodeIterationId;
1038
+ /** Item index (0-based) of the iteration that produced this invocation. */
1039
+ readonly itemIndex?: number;
1040
+ /** When set, this invocation was produced inside a sub-agent triggered by the named parent invocation. */
1041
+ readonly parentInvocationId?: ConnectionInvocationId;
896
1042
  }
897
- interface TelemetryScope {
898
- readonly traceId?: string;
899
- readonly spanId?: string;
900
- readonly costTracking?: CostTrackingTelemetry;
901
- addSpanEvent(args: TelemetrySpanEventRecord): Promise<void> | void;
902
- recordMetric(args: TelemetryMetricRecord): Promise<void> | void;
903
- attachArtifact(args: TelemetryArtifactAttachment): Promise<TelemetryArtifactReference> | TelemetryArtifactReference;
1043
+ /** Arguments for appending a {@link ConnectionInvocationRecord} (engine fills run/workflow ids and timestamps). */
1044
+ type ConnectionInvocationAppendArgs = Readonly<{
1045
+ invocationId: ConnectionInvocationId;
1046
+ connectionNodeId: NodeId;
1047
+ parentAgentNodeId: NodeId;
1048
+ parentAgentActivationId: NodeActivationId;
1049
+ status: NodeExecutionStatus;
1050
+ managedInput?: JsonValue;
1051
+ managedOutput?: JsonValue;
1052
+ statusLabel?: string;
1053
+ subjectName?: string;
1054
+ error?: NodeExecutionError;
1055
+ queuedAt?: string;
1056
+ startedAt?: string;
1057
+ finishedAt?: string;
1058
+ iterationId?: NodeIterationId;
1059
+ itemIndex?: number;
1060
+ parentInvocationId?: ConnectionInvocationId;
1061
+ }>;
1062
+ interface RunCurrentState {
1063
+ outputsByNode: Record<NodeId, NodeOutputs>;
1064
+ nodeSnapshotsByNodeId: Record<NodeId, NodeExecutionSnapshot>;
1065
+ /** Append-only history of connection-scoped invocations (LLM/tool) for inspector and canvas. */
1066
+ connectionInvocations?: ReadonlyArray<ConnectionInvocationRecord>;
1067
+ mutableState?: PersistedMutableRunState;
904
1068
  }
905
- interface TelemetrySpanScope extends TelemetryScope {
906
- readonly traceId: string;
907
- readonly spanId: string;
908
- end(args?: TelemetrySpanEnd): Promise<void> | void;
1069
+ interface CurrentStateExecutionRequest {
1070
+ workflow: WorkflowDefinition;
1071
+ items?: Items;
1072
+ parent?: ParentExecutionRef;
1073
+ executionOptions?: RunExecutionOptions;
1074
+ workflowSnapshot?: PersistedWorkflowSnapshot;
1075
+ mutableState?: PersistedMutableRunState;
1076
+ currentState?: RunCurrentState;
1077
+ stopCondition?: RunStopCondition;
1078
+ reset?: RunStateResetRequest;
1079
+ }
1080
+ type RunStatus = "running" | "pending" | "completed" | "failed" | "suspended" | "halted";
1081
+ /** Reason a run transitioned to {@link RunStatus} `"halted"`. */
1082
+ type RunHaltReason = "hitl-rejected" | "hitl-timeout" | "hitl-cancelled";
1083
+ interface RunSummary {
1084
+ runId: RunId;
1085
+ workflowId: WorkflowId;
1086
+ startedAt: string;
1087
+ status: RunStatus;
909
1088
  /**
910
- * Lift this span into a {@link NodeExecutionTelemetry} scoped to a different (nodeId, activationId).
911
- * Children created via the returned telemetry's `startChildSpan` get this span as their parent.
912
- *
913
- * Used at the sub-agent boundary so that nested runtime telemetry parents under the agent.tool.call
914
- * span instead of the orchestrator's node-level span.
1089
+ * Test-case status for runs dispatched as part of a TestSuiteRun. Carries the
1090
+ * assertion-rollup-corrected outcome the test orchestrator persists onto the row, so the
1091
+ * executions list can show "failed" for a run whose workflow completed cleanly but whose
1092
+ * assertions caught regressions. Absent for non-test runs and legacy rows.
915
1093
  */
916
- asNodeTelemetry(args: Readonly<{
917
- nodeId: NodeId;
918
- activationId: NodeActivationId;
919
- }>): NodeExecutionTelemetry;
920
- }
921
- interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
922
- startChildSpan(args: TelemetryChildSpanStart): TelemetrySpanScope;
1094
+ testCaseStatus?: TestCaseRunStatus;
1095
+ /** ISO timestamp when the run finished (derived from node snapshots or store `updatedAt`); omit while running/pending. */
1096
+ finishedAt?: string;
1097
+ parent?: ParentExecutionRef;
1098
+ executionOptions?: RunExecutionOptions;
923
1099
  }
924
- interface ExecutionTelemetry extends TelemetryScope {
925
- readonly traceId: string;
926
- readonly spanId: string;
927
- forNode(args: Readonly<{
928
- nodeId: NodeId;
929
- activationId: NodeActivationId;
930
- }>): NodeExecutionTelemetry;
1100
+ interface PendingNodeExecution {
1101
+ runId: RunId;
1102
+ activationId: NodeActivationId;
1103
+ workflowId: WorkflowId;
1104
+ nodeId: NodeId;
1105
+ itemsIn: number;
1106
+ inputsByPort: NodeInputsByPort;
1107
+ receiptId: string;
1108
+ queue?: string;
1109
+ batchId?: string;
1110
+ enqueuedAt: string;
931
1111
  }
932
- //#endregion
933
- //#region ../core/src/contracts/executionPersistenceContracts.d.ts
934
- /** Canonical id for persisted execution rows (activation or connection invocation). */
935
- type ExecutionInstanceId = string;
936
- /** Batch grouping for planner activations. */
937
- type BatchId = string;
938
- type PersistedExecutionInstanceKind = "workflowNodeActivation" | "connectionInvocation";
939
- type ConnectionInvocationKind = "languageModel" | "tool" | "nestedAgent";
940
- interface WorkflowRunDetailDto {
941
- readonly runId: RunId;
942
- readonly workflowId: WorkflowId;
943
- readonly startedAt: string;
944
- readonly finishedAt?: string;
945
- readonly status: RunStatus;
946
- readonly workflowSnapshot?: PersistedWorkflowSnapshot;
947
- readonly mutableState?: PersistedMutableRunState;
948
- readonly slotStates: ReadonlyArray<SlotExecutionStateDto>;
949
- readonly executionInstances: ReadonlyArray<ExecutionInstanceDto>;
950
- readonly iterations?: ReadonlyArray<RunIterationDto>;
1112
+ interface PersistedRunSchedulingState {
1113
+ pending?: PendingNodeExecution;
1114
+ queue: RunQueueEntry[];
951
1115
  }
952
- /**
953
- * Per-item iteration projected from connection invocations and node activations.
954
- *
955
- * One iteration = one item processed by an agent within an activation. Multiple invocations
956
- * (LLM rounds, tool calls) belonging to the same iteration share the iterationId.
957
- */
958
- interface RunIterationDto {
959
- readonly iterationId: string;
960
- readonly agentNodeId: NodeId;
1116
+ /** One persisted suspension entry per suspended item. */
1117
+ interface PersistedSuspensionEntry {
1118
+ /** Opaque task identifier (UUID v4). */
1119
+ readonly taskId: string;
1120
+ readonly nodeId: NodeId;
961
1121
  readonly activationId: NodeActivationId;
962
1122
  readonly itemIndex: number;
963
- readonly itemSummary?: string;
964
- readonly status: NodeExecutionStatus;
965
- readonly startedAt?: string;
966
- readonly finishedAt?: string;
967
- readonly invocationIds: ReadonlyArray<string>;
968
- readonly parentInvocationId?: string;
969
- /** Estimated cost rolled up from telemetry cost metric points, keyed by ISO currency code (e.g. "USD"). Values are minor units (cents-of-cents per the metric's `cost.currency_scale`). */
970
- readonly estimatedCostMinorByCurrency?: Readonly<Record<string, number>>;
971
- /** Currency scale (denominator) per currency, when present on the metric points. Joined with `estimatedCostMinorByCurrency` to format human-readable amounts. */
972
- readonly estimatedCostCurrencyScaleByCurrency?: Readonly<Record<string, number>>;
973
- }
974
- interface SlotExecutionStateDto {
975
- readonly slotNodeId: NodeId;
976
- readonly latestInstanceId?: ExecutionInstanceId;
977
- readonly latestTerminalInstanceId?: ExecutionInstanceId;
978
- readonly latestRunningInstanceId?: ExecutionInstanceId;
979
- readonly status?: NodeExecutionStatus;
980
- readonly invocationCount: number;
981
- readonly runCount: number;
1123
+ /** SHA-256 hex digest of the decision schema JSON (for schema-drift detection). */
1124
+ readonly decisionSchemaHash: string;
1125
+ /** Serialized return value from `SuspensionRequest.deliver` (stored on the HumanTask row). */
1126
+ readonly deliveryRef: JsonValue;
1127
+ /** ISO timestamp when the task expires. */
1128
+ readonly timeoutAt: string;
1129
+ readonly onTimeout: "halt" | "auto-accept";
982
1130
  }
983
- interface ExecutionInstanceDto {
984
- readonly instanceId: ExecutionInstanceId;
985
- readonly slotNodeId: NodeId;
986
- readonly workflowNodeId: NodeId;
987
- readonly parentInstanceId?: ExecutionInstanceId;
988
- readonly kind: PersistedExecutionInstanceKind;
989
- readonly connectionKind?: ConnectionInvocationKind;
990
- readonly runIndex: number;
991
- readonly batchId: BatchId;
992
- readonly activationId?: NodeActivationId;
993
- readonly status: NodeExecutionStatus;
994
- readonly queuedAt?: string;
995
- readonly startedAt?: string;
996
- readonly finishedAt?: string;
997
- readonly itemCount: number;
998
- readonly inputJson?: JsonValue;
999
- readonly outputJson?: JsonValue;
1000
- readonly error?: Readonly<NodeExecutionError>;
1001
- /** Per-item iteration that produced this instance. Set on connectionInvocation rows produced inside per-item runnable loops. */
1002
- readonly iterationId?: string;
1003
- /** Item index (0-based) of the iteration. */
1004
- readonly itemIndex?: number;
1005
- /** Parent invocation id when this instance was emitted by a sub-agent triggered by an outer LLM/tool call. */
1006
- readonly parentInvocationId?: string;
1131
+ /**
1132
+ * When a node is re-activated after suspension, the engine writes the resume context here
1133
+ * so `NodeExecutionRequestHandlerService` can splice `resumeContext` into ctx.
1134
+ * Cleared once the re-activation is consumed.
1135
+ */
1136
+ interface PendingResumeEntry {
1137
+ readonly activationId: NodeActivationId;
1138
+ readonly nodeId: NodeId;
1007
1139
  /**
1008
- * When this instance is a SubWorkflow node activation, the run id of the child run it spawned.
1009
- * Used by the UI to deep-link directly to the child execution.
1140
+ * Typed as `unknown` here to avoid a circular import between runTypes runtimeTypes.
1141
+ * `NodeExecutionRequestHandlerService` casts this to `ResumeContext` from runtimeTypes.
1010
1142
  */
1011
- readonly childRunId?: string;
1143
+ readonly resumeContext: unknown;
1012
1144
  }
1013
- //#endregion
1014
- //#region ../core/src/contracts/webhookTypes.d.ts
1015
- type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
1016
- /** Match for an incoming HTTP request: user-defined URL segment + workflow trigger node. */
1017
- interface WebhookInvocationMatch {
1018
- /** Same value as the webhook trigger's configured endpoint key (URL segment under the webhook base path). */
1019
- endpointPath: string;
1145
+ interface PersistedRunState {
1146
+ runId: RunId;
1020
1147
  workflowId: WorkflowId;
1021
- nodeId: NodeId;
1022
- methods: ReadonlyArray<HttpMethod>;
1023
- parseJsonBody?: (body: unknown) => unknown;
1148
+ startedAt: string;
1149
+ /** Canonical terminal time for listings and retention when persisted on the run root. */
1150
+ finishedAt?: string;
1151
+ /** Optimistic concurrency / CAS on the run aggregate (repository may increment on save). */
1152
+ revision?: number;
1153
+ parent?: ParentExecutionRef;
1154
+ executionOptions?: RunExecutionOptions;
1155
+ control?: PersistedRunControlState;
1156
+ workflowSnapshot?: PersistedWorkflowSnapshot;
1157
+ mutableState?: PersistedMutableRunState;
1158
+ /** Frozen at createRun from workflow + runtime defaults for prune/storage decisions. */
1159
+ policySnapshot?: PersistedRunPolicySnapshot;
1160
+ /** Successful node completions so far (for activation budget). */
1161
+ engineCounters?: EngineRunCounters;
1162
+ status: RunStatus;
1163
+ /** Populated when `status === "halted"` to discriminate why the run was halted. */
1164
+ reason?: RunHaltReason;
1165
+ pending?: PendingNodeExecution;
1166
+ queue: RunQueueEntry[];
1167
+ outputsByNode: Record<NodeId, NodeOutputs>;
1168
+ nodeSnapshotsByNodeId: Record<NodeId, NodeExecutionSnapshot>;
1169
+ /** Append-only history of connection invocations (LLM/tool) nested under owning nodes. */
1170
+ connectionInvocations?: ReadonlyArray<ConnectionInvocationRecord>;
1171
+ /**
1172
+ * One entry per outstanding HITL suspension (per-item).
1173
+ * Present and non-empty iff `status === "suspended"`.
1174
+ */
1175
+ suspension?: ReadonlyArray<PersistedSuspensionEntry>;
1176
+ /**
1177
+ * Written by `resumeRun()` so `NodeExecutionRequestHandlerService` can splice `resumeContext`
1178
+ * into the ctx when re-executing the suspended node. Cleared once consumed.
1179
+ */
1180
+ pendingResume?: PendingResumeEntry;
1024
1181
  }
1025
- /** Result of resolving an HTTP method + endpoint path against the catalog webhook index (404 vs 405 vs match). */
1026
- type WebhookTriggerResolution = {
1027
- status: "notFound";
1182
+ interface WorkflowExecutionRepository {
1183
+ createRun(args: {
1184
+ runId: RunId;
1185
+ workflowId: WorkflowId;
1186
+ startedAt: string;
1187
+ parent?: ParentExecutionRef;
1188
+ executionOptions?: RunExecutionOptions;
1189
+ control?: PersistedRunControlState;
1190
+ workflowSnapshot?: PersistedWorkflowSnapshot;
1191
+ mutableState?: PersistedMutableRunState;
1192
+ policySnapshot?: PersistedRunPolicySnapshot;
1193
+ engineCounters?: EngineRunCounters;
1194
+ }): Promise<void>;
1195
+ load(runId: RunId): Promise<PersistedRunState | undefined>;
1196
+ loadSchedulingState(runId: RunId): Promise<PersistedRunSchedulingState | undefined>;
1197
+ save(state: PersistedRunState): Promise<void>;
1198
+ deleteRun?(runId: RunId): Promise<void>;
1199
+ }
1200
+ /** Runs eligible for retention-based pruning (completed or failed, older than cutoff). */
1201
+ interface RunPruneCandidate {
1202
+ readonly runId: RunId;
1203
+ readonly workflowId: WorkflowId;
1204
+ readonly startedAt: string;
1205
+ readonly finishedAt: string;
1206
+ }
1207
+ type RunResult = {
1208
+ runId: RunId;
1209
+ workflowId: WorkflowId;
1210
+ startedAt: string;
1211
+ status: "completed";
1212
+ outputs: Items;
1028
1213
  } | {
1029
- status: "methodNotAllowed";
1030
- match: WebhookInvocationMatch;
1214
+ runId: RunId;
1215
+ workflowId: WorkflowId;
1216
+ startedAt: string;
1217
+ status: "pending";
1218
+ pending: PendingNodeExecution;
1031
1219
  } | {
1032
- status: "ok";
1033
- match: WebhookInvocationMatch;
1220
+ runId: RunId;
1221
+ workflowId: WorkflowId;
1222
+ startedAt: string;
1223
+ status: "failed";
1224
+ error: {
1225
+ message: string;
1226
+ };
1227
+ } | {
1228
+ runId: RunId;
1229
+ workflowId: WorkflowId;
1230
+ startedAt: string;
1231
+ status: "halted";
1232
+ reason: RunHaltReason;
1034
1233
  };
1234
+ type WebhookRunResult = Readonly<{
1235
+ runId: RunId;
1236
+ workflowId: WorkflowId;
1237
+ startedAt: string;
1238
+ runStatus: "pending" | "completed";
1239
+ response: Items;
1240
+ }>;
1241
+ interface PersistedWorkflowTokenRegistryLike {
1242
+ register(type: TypeToken<unknown>, packageId: string, persistedNameOverride?: string): string;
1243
+ getTokenId(type: TypeToken<unknown>): string | undefined;
1244
+ resolve(tokenId: string): TypeToken<unknown> | undefined;
1245
+ registerFromWorkflows?(workflows: ReadonlyArray<WorkflowDefinition>): void;
1246
+ }
1247
+ //#endregion
1248
+ //#region ../core/src/contracts/workflowTypes.d.ts
1249
+ type NodeIdRef<TJson = unknown> = NodeId & Readonly<{
1250
+ __codemationNodeJson?: TJson;
1251
+ }>;
1252
+ type NodeKind = "trigger" | "node";
1253
+ type JsonPrimitive = string | number | boolean | null;
1254
+ interface JsonObject {
1255
+ readonly [key: string]: JsonValue;
1256
+ }
1257
+ type JsonValue = JsonPrimitive | JsonObject | JsonArray;
1258
+ type JsonArray = ReadonlyArray<JsonValue>;
1259
+ interface Edge {
1260
+ from: {
1261
+ nodeId: NodeId;
1262
+ output: OutputPortKey;
1263
+ };
1264
+ to: {
1265
+ nodeId: NodeId;
1266
+ input: InputPortKey;
1267
+ };
1268
+ }
1035
1269
  /**
1036
- * Resolves webhook routes from workflow definitions (catalog-backed index, no registration at trigger setup).
1270
+ * Named connection from a parent node to child nodes that exist in {@link WorkflowDefinition.nodes}
1271
+ * but are not traversed by the main execution graph. Parents are commonly executable nodes, but may
1272
+ * also be connection-owned nodes for recursive agent attachments.
1037
1273
  */
1038
- interface WebhookTriggerMatcher {
1039
- match(args: {
1040
- endpointPath: string;
1041
- method: HttpMethod;
1042
- }): WebhookInvocationMatch | undefined;
1043
- lookup(endpointPath: string): WebhookInvocationMatch | undefined;
1044
- onEngineWorkflowsLoaded?(): void;
1045
- onEngineStopped?(): void;
1046
- /** Rebuild route index after activation changes without stopping the engine. */
1047
- reloadWebhookRoutes?(): void;
1274
+ interface WorkflowNodeConnection {
1275
+ readonly parentNodeId: NodeId;
1276
+ readonly connectionName: NodeConnectionName;
1277
+ readonly childNodeIds: ReadonlyArray<NodeId>;
1048
1278
  }
1049
- //#endregion
1050
- //#region ../core/src/contracts/mcpTypes.d.ts
1051
- type McpServerTransport = "http";
1052
- interface McpServerDeclaration {
1053
- /** Globally unique slug, e.g. "gmail". Workflow authors reference this. */
1054
- id: string;
1055
- displayName: string;
1056
- description: string;
1057
- transport: McpServerTransport;
1058
- url: string;
1279
+ interface WorkflowDefinition {
1280
+ id: WorkflowId;
1281
+ name: string;
1282
+ nodes: NodeDefinition[];
1283
+ edges: Edge[];
1059
1284
  /**
1060
- * Credential types accepted by this MCP server, matching CredentialRequirement.acceptedTypes.
1061
- * Absent or empty means no credential is required.
1285
+ * Optional metadata: which nodes are connection-owned children (e.g. AI agent `llm` / `tools` slots).
1286
+ * When omitted, all nodes in {@link nodes} are treated as executable for topology.
1062
1287
  */
1063
- acceptedCredentialTypes?: ReadonlyArray<string>;
1288
+ readonly connections?: ReadonlyArray<WorkflowNodeConnection>;
1289
+ /** Directory + file-stem path under a workflow discovery root (for UI grouping only). */
1290
+ discoveryPathSegments?: readonly string[];
1291
+ /** Retention for run JSON and binaries (seconds). Host/env may supply defaults when omitted. */
1292
+ readonly prunePolicy?: WorkflowPrunePolicySpec;
1293
+ /** Whether to keep run data after completion. Host/env may supply defaults when omitted. */
1294
+ readonly storagePolicy?: WorkflowStoragePolicySpec;
1295
+ /** Invoked after a node fails permanently (retries exhausted) and node error handler did not recover. */
1296
+ readonly workflowErrorHandler?: WorkflowErrorHandlerSpec;
1297
+ }
1298
+ interface NodeConfigBase {
1299
+ readonly kind: NodeKind;
1300
+ readonly type: TypeToken<unknown>;
1301
+ readonly name?: string;
1302
+ readonly id?: NodeId;
1303
+ readonly icon?: string;
1304
+ readonly execution?: Readonly<{
1305
+ hint?: "local" | "worker";
1306
+ queue?: string;
1307
+ }>;
1308
+ /** In-process execute retries (runnable nodes). Triggers typically omit this. */
1309
+ readonly retryPolicy?: RetryPolicySpec;
1310
+ /** Recover from execute failures; return outputs to continue, or rethrow to fail the node. */
1311
+ readonly nodeErrorHandler?: NodeErrorHandlerSpec;
1064
1312
  /**
1065
- * Documentation only in MVP. The bind-time validator checks
1066
- * requiredScopes CredentialInstance.scopesGranted.
1313
+ * When true, edges carrying zero items on an output port still schedule single-input downstream nodes.
1314
+ * Decided from the **source** node that produced the (empty) output. Default (false/undefined): empty
1315
+ * main batches skip downstream execution and propagate the empty path.
1067
1316
  */
1068
- requiredScopes?: string[];
1069
- /** Non-secret static headers merged onto every MCP request. */
1070
- staticHeaders?: Record<string, string>;
1317
+ readonly continueWhenEmptyOutput?: boolean;
1071
1318
  /**
1072
- * Overrides for tool descriptions advertised by the MCP server.
1073
- * Applied by the connection pool after tools/list.
1074
- * Key: exact tool name as returned by the server.
1319
+ * Declared I/O port names for canvas authoring (unioned with ports inferred from edges).
1320
+ * Use for dynamic routers (Switch) and future error ports.
1075
1321
  */
1076
- toolDescriptionOverrides?: Record<string, string>;
1077
- }
1078
- //#endregion
1079
- //#region ../core/src/contracts/collectionTypes.d.ts
1080
- /**
1081
- * Represents a typed store for a single collection.
1082
- * All rows include auto-managed id, created_at, and updated_at fields.
1083
- */
1084
- interface CollectionStore<TRow extends Record<string, unknown> = Record<string, unknown>> {
1322
+ readonly declaredOutputPorts?: ReadonlyArray<OutputPortKey>;
1323
+ readonly declaredInputPorts?: ReadonlyArray<InputPortKey>;
1324
+ getCredentialRequirements?(): ReadonlyArray<CredentialRequirement>;
1085
1325
  /**
1086
- * Insert a new row. id, created_at, and updated_at are auto-populated.
1326
+ * Marker: this node emits {@link import("./assertionTypes").AssertionResult}-shaped items on its
1327
+ * `main` port. The TestSuiteOrchestrator (and host-side TestAssertionPersister) listen for
1328
+ * `nodeCompleted` events from nodes with this flag set, and persist their output items as
1329
+ * TestAssertion records (only when the run carries a `testContext`). Set on assertion node
1330
+ * configs (e.g. `AssertionNodeConfig`, `StringEqualsAssertionNodeConfig`).
1087
1331
  */
1088
- insert(row: TRow): Promise<TRow & {
1089
- id: string;
1090
- created_at: Date;
1091
- updated_at: Date;
1092
- }>;
1332
+ readonly emitsAssertions?: true;
1093
1333
  /**
1094
- * Get a single row by id.
1334
+ * Static configuration summary surfaced in the workflow inspector — the design-time
1335
+ * "what does this node do" panel that renders before any run telemetry exists.
1336
+ *
1337
+ * Return 2–6 short label/value pairs derived from this config (method + url for an HTTP
1338
+ * call, model + tool list for an agent, schedule + timezone for a cron trigger, etc.).
1339
+ * Values are truncated by the UI; aim for one line each. Return `undefined` to opt out
1340
+ * — the inspector hides the section when no rows are produced.
1341
+ *
1342
+ * Implement on the config class instance so the function can read sibling config fields.
1343
+ * `defineNode({ inspectorSummary })` plumbs through to this.
1095
1344
  */
1096
- get(id: string): Promise<(TRow & {
1097
- id: string;
1098
- created_at: Date;
1099
- updated_at: Date;
1100
- }) | null>;
1345
+ inspectorSummary?(): ReadonlyArray<NodeInspectorSummaryRow> | undefined;
1346
+ }
1347
+ /**
1348
+ * One row of a node's static configuration summary. See {@link NodeConfigBase.inspectorSummary}.
1349
+ */
1350
+ interface NodeInspectorSummaryRow {
1351
+ readonly label: string;
1352
+ readonly value: string;
1353
+ }
1354
+ declare const runnableNodeInputType: unique symbol;
1355
+ declare const runnableNodeOutputType: unique symbol;
1356
+ declare const triggerNodeOutputType: unique symbol;
1357
+ /**
1358
+ * Runnable node: **`TInputJson`** is what **`inputSchema`** validates on **`item.json`** (the wire payload).
1359
+ * **`TOutputJson`** is emitted `item.json` on outputs.
1360
+ */
1361
+ interface RunnableNodeConfig<TInputJson$1 = unknown, TOutputJson$1 = unknown> extends NodeConfigBase {
1362
+ readonly kind: "node";
1363
+ readonly [runnableNodeInputType]?: TInputJson$1;
1364
+ readonly [runnableNodeOutputType]?: TOutputJson$1;
1101
1365
  /**
1102
- * Find a single row matching the provided filter.
1366
+ * Optional Zod input contract for {@link RunnableNode} when not set on the node class.
1367
+ * Resolution order: node instance `inputSchema`, then config `inputSchema`, then `z.unknown()`.
1103
1368
  */
1104
- findOne(filter: Partial<TRow>): Promise<(TRow & {
1105
- id: string;
1106
- created_at: Date;
1107
- updated_at: Date;
1108
- }) | null>;
1369
+ readonly inputSchema?: ZodType<TInputJson$1>;
1109
1370
  /**
1110
- * List rows with optional pagination and filtering.
1371
+ * When an activation receives **zero** input items, the engine normally runs `execute` zero times.
1372
+ * Set to **`runOnce`** to run `execute` once with an empty `items` batch (and a synthetic wire item for schema parsing).
1373
+ * Used by batch-style callback nodes (built-in `Callback`) so `callback([], ctx)` still runs.
1111
1374
  */
1112
- list(opts?: {
1113
- limit?: number;
1114
- offset?: number;
1115
- where?: Partial<TRow>;
1116
- }): Promise<{
1117
- rows: ReadonlyArray<TRow & {
1118
- id: string;
1119
- created_at: Date;
1120
- updated_at: Date;
1121
- }>;
1122
- total: number;
1123
- }>;
1375
+ readonly emptyBatchExecution?: "skip" | "runOnce";
1376
+ }
1377
+ declare const triggerNodeSetupStateType: unique symbol;
1378
+ interface TriggerNodeConfig<TOutputJson$1 = unknown, TSetupState$1 extends JsonValue | undefined = undefined> extends NodeConfigBase {
1379
+ readonly kind: "trigger";
1380
+ readonly [triggerNodeOutputType]?: TOutputJson$1;
1381
+ readonly [triggerNodeSetupStateType]?: TSetupState$1;
1124
1382
  /**
1125
- * Update a row by id with partial data.
1383
+ * Distinguishes triggers driven by the live activation policy (webhooks, cron, polling) from
1384
+ * triggers driven only by the {@link TestSuiteOrchestrator}. `WorkflowActivation` skips
1385
+ * `"test"` triggers; the orchestrator skips `"live"` triggers. Defaults to `"live"` when omitted.
1126
1386
  */
1127
- update(id: string, patch: Partial<TRow>): Promise<TRow & {
1128
- id: string;
1129
- created_at: Date;
1130
- updated_at: Date;
1131
- }>;
1387
+ readonly triggerKind?: "live" | "test";
1388
+ }
1389
+ type RunnableNodeOutputJson<TConfig extends RunnableNodeConfig<any, any>> = TConfig extends RunnableNodeConfig<any, infer TOutputJson> ? TOutputJson : never;
1390
+ type TriggerNodeOutputJson<TConfig extends TriggerNodeConfig<any, any>> = TConfig extends TriggerNodeConfig<infer TOutputJson, any> ? TOutputJson : never;
1391
+ interface NodeDefinition {
1392
+ id: NodeId;
1393
+ kind: NodeKind;
1394
+ type: TypeToken<unknown>;
1395
+ name?: string;
1396
+ config: NodeConfigBase;
1397
+ }
1398
+ interface NodeRef {
1399
+ id: NodeId;
1400
+ kind: NodeKind;
1401
+ name?: string;
1402
+ }
1403
+ type PairedItemRef = Readonly<{
1404
+ nodeId: NodeId;
1405
+ output: OutputPortKey;
1406
+ itemIndex: number;
1407
+ }>;
1408
+ type BinaryPreviewKind = "image" | "audio" | "video" | "download";
1409
+ type BinaryAttachment = Readonly<{
1410
+ id: string;
1411
+ storageKey: string;
1412
+ mimeType: string;
1413
+ size: number;
1414
+ storageDriver: string;
1415
+ previewKind: BinaryPreviewKind;
1416
+ createdAt: string;
1417
+ runId: RunId;
1418
+ workflowId: WorkflowId;
1419
+ nodeId: NodeId;
1420
+ activationId: NodeActivationId;
1421
+ filename?: string;
1422
+ sha256?: string;
1423
+ }>;
1424
+ type ItemBinary = Readonly<Record<string, BinaryAttachment>>;
1425
+ type Item<TJson = unknown> = Readonly<{
1426
+ json: TJson;
1427
+ binary?: ItemBinary;
1428
+ meta?: Readonly<Record<string, unknown>>;
1429
+ paired?: ReadonlyArray<PairedItemRef>;
1430
+ }>;
1431
+ type Items<TJson = unknown> = ReadonlyArray<Item<TJson>>;
1432
+ type NodeOutputs = Partial<Record<OutputPortKey, Items>>;
1433
+ type RunId = string;
1434
+ type NodeActivationId = string;
1435
+ /**
1436
+ * One per-item iteration of a runnable node's execute loop. Refines `NodeActivationId` for
1437
+ * per-item connection invocations and telemetry. Undefined when the executing node is a batch
1438
+ * node or trigger that does not iterate items.
1439
+ */
1440
+ type NodeIterationId = string;
1441
+ interface ParentExecutionRef {
1442
+ runId: RunId;
1443
+ workflowId: WorkflowId;
1444
+ nodeId: NodeId;
1445
+ /** Subworkflow depth of the **spawning** run (0 = root). Passed when starting a child run. */
1446
+ subworkflowDepth?: number;
1447
+ /** Effective max node activations from the parent run (propagated to child policy merge). */
1448
+ engineMaxNodeActivations?: number;
1449
+ /** Effective max subworkflow depth from the parent run (propagated to child policy merge). */
1450
+ engineMaxSubworkflowDepth?: number;
1132
1451
  /**
1133
- * Delete a row by id. Hard delete only (no soft delete).
1452
+ * Test-suite linkage inherited by the child subworkflow run. Set by whichever node
1453
+ * spawns the subworkflow when its own `ctx.testContext` is present, so assertions
1454
+ * emitted inside a subworkflow land under the correct parent test case.
1134
1455
  */
1135
- delete(id: string): Promise<{
1136
- deleted: boolean;
1137
- }>;
1456
+ testContext?: RunTestContext;
1457
+ }
1458
+ interface RunDataSnapshot {
1459
+ getOutputs(nodeId: NodeId): NodeOutputs | undefined;
1460
+ getOutputItems<TJson = unknown>(nodeId: NodeId | NodeIdRef<TJson>, output?: OutputPortKey): Items<TJson>;
1461
+ getOutputItem<TJson = unknown>(nodeId: NodeId | NodeIdRef<TJson>, itemIndex: number, output?: OutputPortKey): Item<TJson> | undefined;
1462
+ }
1463
+ interface RunIdFactory {
1464
+ makeRunId(): RunId;
1465
+ }
1466
+ interface ActivationIdFactory {
1467
+ makeActivationId(): NodeActivationId;
1468
+ }
1469
+ /** Whether to persist run execution data after the workflow finishes. */
1470
+ type WorkflowStoragePolicyMode = "ALL" | "SUCCESS" | "ERROR" | "NEVER";
1471
+ type WorkflowStoragePolicySpec = WorkflowStoragePolicyMode | TypeToken<WorkflowStoragePolicyResolver>;
1472
+ interface WorkflowStoragePolicyResolver {
1473
+ shouldPersist(args: WorkflowStoragePolicyDecisionArgs): boolean | Promise<boolean>;
1474
+ }
1475
+ interface WorkflowStoragePolicyDecisionArgs {
1476
+ readonly runId: RunId;
1477
+ readonly workflowId: WorkflowId;
1478
+ readonly workflow: WorkflowDefinition;
1479
+ readonly finalStatus: "completed" | "failed";
1480
+ readonly startedAt: string;
1481
+ readonly finishedAt: string;
1482
+ }
1483
+ interface WorkflowPrunePolicySpec {
1484
+ readonly runDataRetentionSeconds?: number;
1485
+ readonly binaryRetentionSeconds?: number;
1486
+ readonly telemetrySpanRetentionSeconds?: number;
1487
+ readonly telemetryArtifactRetentionSeconds?: number;
1488
+ readonly telemetryMetricRetentionSeconds?: number;
1489
+ }
1490
+ interface PersistedRunPolicySnapshot {
1491
+ readonly retentionSeconds?: number;
1492
+ readonly binaryRetentionSeconds?: number;
1493
+ readonly telemetrySpanRetentionSeconds?: number;
1494
+ readonly telemetryArtifactRetentionSeconds?: number;
1495
+ readonly telemetryMetricRetentionSeconds?: number;
1496
+ readonly storagePolicy: WorkflowStoragePolicyMode;
1138
1497
  }
1139
- /**
1140
- * Runtime collections context: keyed by collection name.
1141
- */
1142
- type CollectionsContext = Readonly<Record<string, CollectionStore>>;
1143
- //#endregion
1144
- //#region ../core/src/contracts/workflowActivationPolicy.d.ts
1145
- /**
1146
- * Host-controlled policy: when false, trigger {@link TriggerNode} setup is skipped and webhook routes
1147
- * for that workflow are not registered (see engine trigger runtime + webhook matcher).
1148
- */
1149
- interface WorkflowActivationPolicy {
1150
- isActive(workflowId: WorkflowId): boolean;
1498
+ interface WorkflowErrorHandler {
1499
+ onError(ctx: WorkflowErrorContext): void | Promise<void>;
1151
1500
  }
1152
- //#endregion
1153
- //#region ../core/src/workflow/dsl/workflowBuilderTypes.d.ts
1154
- type AnyRunnableNodeConfig = RunnableNodeConfig<any, any>;
1155
- type AnyTriggerNodeConfig = TriggerNodeConfig<any>;
1156
- type ValidStepSequence<TCurrentJson, TSteps extends ReadonlyArray<AnyRunnableNodeConfig>> = TSteps extends readonly [] ? readonly [] : TSteps extends readonly [infer TFirst, ...infer TRest] ? TFirst extends RunnableNodeConfig<TCurrentJson, infer TNextJson> ? TRest extends ReadonlyArray<AnyRunnableNodeConfig> ? readonly [TFirst, ...ValidStepSequence<TNextJson, TRest>] : never : never : TSteps;
1157
- type StepSequenceOutput<TCurrentJson, TSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined> = TSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? TSteps extends readonly [] ? TCurrentJson : TSteps extends readonly [infer TFirst, ...infer TRest] ? TFirst extends RunnableNodeConfig<TCurrentJson, infer TNextJson> ? TRest extends ReadonlyArray<AnyRunnableNodeConfig> ? StepSequenceOutput<TNextJson, TRest> : never : never : TCurrentJson : TCurrentJson;
1158
- type TypesMatch<TLeft, TRight> = [TLeft] extends [TRight] ? ([TRight] extends [TLeft] ? true : false) : false;
1159
- type BranchOutputGuard<TCurrentJson, TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined, TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined> = TypesMatch<StepSequenceOutput<TCurrentJson, TTrueSteps>, StepSequenceOutput<TCurrentJson, TFalseSteps>> extends true ? unknown : never;
1160
- type BranchStepsArg<TCurrentJson, TSteps extends ReadonlyArray<AnyRunnableNodeConfig>> = TSteps & ValidStepSequence<TCurrentJson, TSteps>;
1161
- type BranchMoreArgs<TCurrentJson, TFirstStep extends RunnableNodeConfig<TCurrentJson, any>, TRestSteps extends ReadonlyArray<AnyRunnableNodeConfig>> = TRestSteps & ValidStepSequence<RunnableNodeOutputJson<TFirstStep>, TRestSteps>;
1162
- type BooleanWhenOverloads<TCurrentJson, TReturn> = {
1163
- <TSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(branch: boolean, steps: BranchStepsArg<TCurrentJson, TSteps>): TReturn;
1164
- <TFirstStep extends RunnableNodeConfig<TCurrentJson, any>, TRestSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(branch: boolean, step: TFirstStep, ...more: BranchMoreArgs<TCurrentJson, TFirstStep, TRestSteps>): TReturn;
1165
- };
1166
- //#endregion
1167
- //#region ../core/src/workflow/dsl/WhenBuilder.d.ts
1168
- declare class WhenBuilder<TCurrentJson> {
1169
- private readonly wf;
1170
- private readonly from;
1171
- private readonly branchPort;
1172
- constructor(wf: WorkflowBuilder, from: NodeRef, branchPort: OutputPortKey);
1173
- addBranch<TSteps extends ReadonlyArray<AnyRunnableNodeConfig>>(steps: TSteps & ValidStepSequence<TCurrentJson, TSteps>): this;
1174
- readonly when: BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>>;
1175
- build(): WorkflowDefinition;
1501
+ interface WorkflowErrorContext {
1502
+ readonly runId: RunId;
1503
+ readonly workflowId: WorkflowId;
1504
+ readonly workflow: WorkflowDefinition;
1505
+ readonly failedNodeId: NodeId;
1506
+ readonly error: Error;
1507
+ readonly startedAt: string;
1508
+ readonly finishedAt: string;
1176
1509
  }
1177
- //#endregion
1178
- //#region ../core/src/workflow/dsl/ChainCursorResolver.d.ts
1179
- type ChainCursorEndpoint = Readonly<{
1180
- node: NodeRef;
1181
- output: OutputPortKey;
1182
- inputPortHint?: InputPortKey;
1183
- }>;
1184
- type ChainCursorWhenOverloads<TCurrentJson> = BooleanWhenOverloads<TCurrentJson, WhenBuilder<TCurrentJson>> & {
1185
- <TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined, TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> | undefined>(branches: Readonly<{
1186
- true?: TTrueSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? BranchStepsArg<TCurrentJson, TTrueSteps> : never;
1187
- false?: TFalseSteps extends ReadonlyArray<AnyRunnableNodeConfig> ? BranchStepsArg<TCurrentJson, TFalseSteps> : never;
1188
- }> & BranchOutputGuard<TCurrentJson, TTrueSteps, TFalseSteps>): ChainCursor<StepSequenceOutput<TCurrentJson, TTrueSteps>>;
1189
- };
1190
- declare class ChainCursor<TCurrentJson> {
1191
- private readonly wf;
1192
- private readonly endpoints;
1193
- constructor(wf: WorkflowBuilder, endpoints: ReadonlyArray<ChainCursorEndpoint>);
1194
- then<TOutputJson$1, TConfig extends RunnableNodeConfig<TCurrentJson, TOutputJson$1>>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>>;
1195
- thenIntoInputHints<TOutputJson$1, TConfig extends RunnableNodeConfig<any, TOutputJson$1>>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>>;
1196
- readonly when: ChainCursorWhenOverloads<TCurrentJson>;
1197
- route<TNextJson$1>(branches: Readonly<Record<OutputPortKey, (branch: ChainCursor<TCurrentJson>) => ChainCursor<TNextJson$1> | undefined>>): ChainCursor<TNextJson$1>;
1198
- build(): WorkflowDefinition;
1199
- private resolveSharedInputPortHint;
1510
+ type WorkflowErrorHandlerSpec = TypeToken<WorkflowErrorHandler> | WorkflowErrorHandler;
1511
+ interface NodeErrorHandlerArgs<TConfig extends NodeConfigBase = NodeConfigBase> {
1512
+ readonly kind: "single" | "multi";
1513
+ readonly items: Items;
1514
+ readonly inputsByPort: Readonly<Record<InputPortKey, Items>> | undefined;
1515
+ readonly ctx: NodeExecutionContext<TConfig>;
1516
+ readonly error: Error;
1200
1517
  }
1201
- //#endregion
1202
- //#region ../core/src/workflow/dsl/WorkflowBuilder.d.ts
1203
- declare class WorkflowBuilder {
1204
- private readonly meta;
1205
- private readonly options?;
1206
- private readonly nodes;
1207
- private readonly edges;
1208
- constructor(meta: {
1209
- id: WorkflowId;
1210
- name: string;
1211
- }, options?: Readonly<Record<string, never>> | undefined);
1212
- private add;
1213
- private connect;
1214
- trigger<TConfig extends AnyTriggerNodeConfig>(config: TConfig): ChainCursor<TriggerNodeOutputJson<TConfig>>;
1215
- start<TConfig extends AnyRunnableNodeConfig>(config: TConfig): ChainCursor<RunnableNodeOutputJson<TConfig>>;
1216
- build(): WorkflowDefinition;
1217
- private validateNodeIds;
1518
+ interface NodeErrorHandler {
1519
+ handle<TConfig extends NodeConfigBase>(args: NodeErrorHandlerArgs<TConfig>): Promise<NodeOutputs>;
1218
1520
  }
1521
+ type NodeErrorHandlerSpec = TypeToken<NodeErrorHandler> | NodeErrorHandler;
1219
1522
  //#endregion
1220
- //#region ../core/src/events/runEvents.d.ts
1523
+ //#region ../core/src/contracts/runtimeTypes.d.ts
1524
+ /** Opaque unique identifier for a single HumanTask instance. */
1525
+ type HumanTaskId = string;
1221
1526
  /**
1222
- * Outcome of a single test case (one workflow run dispatched by the test-suite orchestrator).
1223
- * - `running`: workflow still in flight
1224
- * - `succeeded`: workflow completed AND all assertions passed (or no assertions)
1225
- * - `failed`: workflow failed OR (workflow completed but ≥1 assertion failed)
1226
- * - `errored` / `cancelled`: workflow itself errored or was cancelled
1527
+ * Minimal handle handed to the `deliver` callback so it can route to the correct
1528
+ * inbox channel.
1227
1529
  */
1228
- type TestCaseRunStatus = "running" | "succeeded" | "failed" | "errored" | "cancelled";
1229
- /** Aggregate outcome of a TestSuiteRun. */
1230
- type TestSuiteRunStatus = "succeeded" | "failed" | "partial" | "errored" | "cancelled";
1231
- type RunEvent = Readonly<{
1232
- kind: "runCreated";
1233
- runId: RunId;
1234
- workflowId: WorkflowId;
1235
- parent?: ParentExecutionRef;
1236
- at: string;
1237
- }> | Readonly<{
1238
- kind: "runSaved";
1239
- runId: RunId;
1240
- workflowId: WorkflowId;
1241
- parent?: ParentExecutionRef;
1242
- at: string;
1243
- state: PersistedRunState;
1244
- }> | Readonly<{
1245
- kind: "nodeQueued";
1246
- runId: RunId;
1247
- workflowId: WorkflowId;
1248
- parent?: ParentExecutionRef;
1249
- at: string;
1250
- snapshot: NodeExecutionSnapshot;
1251
- }> | Readonly<{
1252
- kind: "nodeStarted";
1253
- runId: RunId;
1254
- workflowId: WorkflowId;
1255
- parent?: ParentExecutionRef;
1256
- at: string;
1257
- snapshot: NodeExecutionSnapshot;
1258
- }> | Readonly<{
1259
- kind: "nodeCompleted";
1260
- runId: RunId;
1261
- workflowId: WorkflowId;
1262
- parent?: ParentExecutionRef;
1263
- at: string;
1264
- snapshot: NodeExecutionSnapshot;
1265
- }> | Readonly<{
1266
- kind: "nodeFailed";
1267
- runId: RunId;
1268
- workflowId: WorkflowId;
1269
- parent?: ParentExecutionRef;
1270
- at: string;
1271
- snapshot: NodeExecutionSnapshot;
1272
- }> | Readonly<{
1273
- kind: "connectionInvocationStarted";
1274
- runId: RunId;
1275
- workflowId: WorkflowId;
1276
- parent?: ParentExecutionRef;
1277
- at: string;
1278
- record: ConnectionInvocationRecord;
1279
- }> | Readonly<{
1280
- kind: "connectionInvocationCompleted";
1281
- runId: RunId;
1282
- workflowId: WorkflowId;
1283
- parent?: ParentExecutionRef;
1284
- at: string;
1285
- record: ConnectionInvocationRecord;
1286
- }> | Readonly<{
1287
- kind: "connectionInvocationFailed";
1288
- runId: RunId;
1289
- workflowId: WorkflowId;
1290
- parent?: ParentExecutionRef;
1291
- at: string;
1292
- record: ConnectionInvocationRecord;
1293
- }> | Readonly<{
1294
- kind: "testSuiteStarted";
1295
- testSuiteRunId: TestSuiteRunId;
1296
- workflowId: WorkflowId;
1297
- triggerNodeId: string;
1298
- triggerNodeName?: string;
1299
- concurrency: number;
1300
- at: string;
1301
- }> | Readonly<{
1302
- kind: "testSuiteFinished";
1303
- testSuiteRunId: TestSuiteRunId;
1304
- workflowId: WorkflowId;
1305
- status: TestSuiteRunStatus;
1306
- totalCases: number;
1307
- passedCases: number;
1308
- failedCases: number;
1309
- at: string;
1310
- }> | Readonly<{
1311
- kind: "testCaseStarted";
1312
- testSuiteRunId: TestSuiteRunId;
1313
- testCaseIndex: number;
1314
- runId: RunId;
1315
- workflowId: WorkflowId;
1316
- testCaseLabel?: string;
1317
- at: string;
1318
- }> | Readonly<{
1319
- kind: "testCaseCompleted";
1320
- testSuiteRunId: TestSuiteRunId;
1321
- testCaseIndex: number;
1322
- runId: RunId;
1323
- workflowId: WorkflowId;
1324
- status: TestCaseRunStatus;
1325
- at: string;
1326
- }>;
1327
- interface RunEventSubscription {
1328
- close(): Promise<void>;
1530
+ interface HumanTaskHandle {
1531
+ readonly taskId: HumanTaskId;
1532
+ readonly runId: string;
1533
+ readonly nodeId: string;
1534
+ readonly expiresAt: Date;
1535
+ /** TODO: real signed URL; placeholder empty string for now. */
1536
+ readonly resumeUrl: string;
1537
+ /**
1538
+ * Arbitrary JSON metadata copied from `SuspensionRequest.request.metadata` at suspension time.
1539
+ * Used by the agent runtime to round-trip the `agentCheckpoint` back to the
1540
+ * resumed node via `ctx.resumeContext.task.metadata`.
1541
+ */
1542
+ readonly metadata?: Readonly<Record<string, JsonValue>>;
1329
1543
  }
1330
- interface RunEventBus {
1331
- publish(event: RunEvent): Promise<void>;
1332
- subscribe(onEvent: (event: RunEvent) => void): Promise<RunEventSubscription>;
1333
- subscribeToWorkflow(workflowId: WorkflowId, onEvent: (event: RunEvent) => void): Promise<RunEventSubscription>;
1544
+ /** Identity of the person who made a decision on the task. */
1545
+ interface HumanTaskActor {
1546
+ readonly actorId: string;
1547
+ readonly displayName?: string;
1334
1548
  }
1335
- //#endregion
1336
- //#region ../core/src/policies/executionLimits/EngineExecutionLimitsPolicy.d.ts
1337
- interface EngineExecutionLimitsPolicyConfig {
1338
- readonly defaultMaxNodeActivations: number;
1339
- readonly hardMaxNodeActivations: number;
1340
- readonly defaultMaxSubworkflowDepth: number;
1341
- readonly hardMaxSubworkflowDepth: number;
1549
+ /**
1550
+ * Resume context injected into `NodeExecutionContext` when the engine re-activates
1551
+ * a previously suspended node. `defineHumanApprovalNode` wraps this with parsed
1552
+ * `TDecision`; at the engine layer `decision.value` is `unknown`.
1553
+ */
1554
+ interface ResumeContext {
1555
+ readonly decision: Readonly<{
1556
+ kind: "decided";
1557
+ value: unknown;
1558
+ actor: HumanTaskActor;
1559
+ decidedAt: Date;
1560
+ }> | Readonly<{
1561
+ kind: "timed_out";
1562
+ at: Date;
1563
+ }> | Readonly<{
1564
+ kind: "auto_accepted";
1565
+ at: Date;
1566
+ }>;
1567
+ readonly delivery: JsonValue;
1568
+ readonly task: HumanTaskHandle;
1342
1569
  }
1343
- //#endregion
1344
- //#region ../core/src/contracts/runtimeTypes.d.ts
1345
1570
  interface WorkflowRepository {
1346
1571
  list(): ReadonlyArray<WorkflowDefinition>;
1347
1572
  get(workflowId: WorkflowId): WorkflowDefinition | undefined;
@@ -1461,6 +1686,14 @@ interface ExecutionContext {
1461
1686
  * Collections registered in the codemation config, keyed by collection name.
1462
1687
  */
1463
1688
  readonly collections?: CollectionsContext;
1689
+ /**
1690
+ * Resolve a DI token from the host container.
1691
+ * Allows nodes to reach host-side services (e.g. `InboxChannelResolverToken`)
1692
+ * without importing host code. Wired by `DefaultExecutionContextFactory`; throws
1693
+ * a clear error when no resolver is configured (e.g. in unit tests that don't
1694
+ * set up the full container).
1695
+ */
1696
+ resolve<T>(token: TypeToken<T>): T;
1464
1697
  }
1465
1698
  interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfigBase> extends ExecutionContext {
1466
1699
  nodeId: NodeId;
@@ -1468,6 +1701,11 @@ interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfigBase>
1468
1701
  config: TConfig;
1469
1702
  telemetry: NodeExecutionTelemetry;
1470
1703
  binary: NodeBinaryAttachmentService;
1704
+ /**
1705
+ * Present when this node activation is a HITL resume.
1706
+ * The node checks `ctx.resumeContext !== undefined` and takes the resume branch.
1707
+ */
1708
+ resumeContext?: ResumeContext;
1471
1709
  }
1472
1710
  interface NodeExecutionRequest {
1473
1711
  runId: RunId;
@@ -1515,35 +1753,6 @@ interface WorkflowSnapshotResolver {
1515
1753
  }): WorkflowDefinition | undefined;
1516
1754
  }
1517
1755
  //#endregion
1518
- //#region ../core/src/contracts/itemExpr.d.ts
1519
- declare const ITEM_EXPR_BRAND: unique symbol;
1520
- type ItemExprResolvedContext = Readonly<{
1521
- runId: RunId;
1522
- workflowId: WorkflowId;
1523
- nodeId: NodeId;
1524
- activationId: NodeActivationId;
1525
- data: RunDataSnapshot;
1526
- }>;
1527
- /**
1528
- * Context aligned with former {@link ItemInputMapperContext} — use **`data`** to read any completed upstream node.
1529
- */
1530
- type ItemExprContext = ItemExprResolvedContext;
1531
- type ItemExprArgs<TItemJson = unknown> = Readonly<{
1532
- item: Item<TItemJson>;
1533
- itemIndex: number;
1534
- items: Items<TItemJson>;
1535
- ctx: ItemExprContext;
1536
- }>;
1537
- type ItemExprCallback<T, TItemJson = unknown> = (args: ItemExprArgs<TItemJson>) => T | Promise<T>;
1538
- type ItemExpr<T, TItemJson = unknown> = Readonly<{
1539
- readonly [ITEM_EXPR_BRAND]: true;
1540
- readonly fn: ItemExprCallback<T, TItemJson>;
1541
- }>;
1542
- //#endregion
1543
- //#region ../core/src/contracts/params.d.ts
1544
- type Expr<T, TItemJson = unknown> = ItemExpr<T, TItemJson>;
1545
- type ParamDeep<T, TItemJson = unknown> = Expr<T, TItemJson> | (T extends readonly (infer U)[] ? ReadonlyArray<ParamDeep<U, TItemJson>> : never) | (T extends object ? { [K in keyof T]: ParamDeep<T[K], TItemJson> } : T);
1546
- //#endregion
1547
1756
  //#region ../core/src/ai/AiHost.d.ts
1548
1757
  interface AgentCanvasPresentation<TIcon extends string = string> {
1549
1758
  readonly label?: string;
@@ -1633,5 +1842,5 @@ interface ChatModelFactory<TConfig extends ChatModelConfig = ChatModelConfig> {
1633
1842
  }>): Promise<ChatLanguageModel> | ChatLanguageModel;
1634
1843
  }
1635
1844
  //#endregion
1636
- export { PersistedRunState as $, WorkflowRunDetailDto as A, NodeActivationId as B, AnyRunnableNodeConfig as C, CredentialSetupStatus as Ct, WebhookInvocationMatch as D, CredentialTypeRegistry as Dt, HttpMethod as E, CredentialTypeId as Et, ActivationIdFactory as F, PersistedRunPolicySnapshot as G, NodeInspectorSummaryRow as H, BinaryAttachment as I, RunnableNodeConfig as J, RunId as K, Item as L, TelemetryAttributes as M, TelemetryMetricRecord as N, WebhookTriggerMatcher as O, NodeId as Ot, TelemetrySpanEventRecord as P, NodeInputsByPort as Q, Items as R, ChainCursor as S, CredentialSessionService as St, McpServerDeclaration as T, CredentialTypeDefinition as Tt, NodeOutputs as U, NodeDefinition as V, ParentExecutionRef as W, WorkflowDefinition as X, RunnableNodeOutputJson as Y, CurrentStateExecutionRequest as Z, EngineExecutionLimitsPolicyConfig as _, CredentialJsonRecord as _t, ParamDeep as a, RunStatus as at, TestCaseRunStatus as b, CredentialRequirement as bt, NodeActivationContinuation as c, WebhookRunResult as ct, NodeExecutionRequestHandler as d, CredentialBinding as dt, PersistedWorkflowTokenRegistryLike as et, NodeExecutionScheduler as f, CredentialBindingKey as ft, TypeToken as g, CredentialInstanceRecord as gt, Container as h, CredentialInstanceId as ht, ToolConfig as i, RunResult as it, TelemetryArtifactAttachment as j, WebhookTriggerResolution as k, WorkflowId as kt, NodeExecutionContext as l, WorkflowExecutionRepository as lt, WorkflowSnapshotResolver as m, CredentialHealth as mt, AgentMessageConfig as n, RunExecutionOptions as nt, BinaryStorage as o, RunStopCondition as ot, WorkflowRepository as p, CredentialFieldSchema as pt, RunIdFactory as q, ChatModelConfig as r, RunPruneCandidate as rt, LiveWorkflowRepository as s, RunSummary as st, AgentGuardrailConfig as t, RunCurrentState as tt, NodeExecutionRequest as u, AnyCredentialType as ut, RunEvent as v, CredentialMaterialSourceKind as vt, WorkflowActivationPolicy as w, CredentialType as wt, TestSuiteRunStatus as x, CredentialSessionFactoryArgs as xt, RunEventBus as y, CredentialOAuth2AuthDefinition as yt, JsonValue as z };
1637
- //# sourceMappingURL=ItemsInputNormalizer-_RwIfRIQ.d.ts.map
1845
+ export { DefinedNode as $, WorkflowDefinition as A, NodeId as At, RunStopCondition as B, NodeOutputs as C, WebhookTriggerMatcher as Ct, RunIdFactory as D, TelemetryAttributes as Dt, RunId as E, TelemetryArtifactAttachment as Et, RunCurrentState as F, TypeToken as G, WebhookRunResult as H, RunExecutionOptions as I, RunEventBus as J, EngineExecutionLimitsPolicyConfig as K, RunPruneCandidate as L, NodeInputsByPort as M, PersistedRunState as N, RunnableNodeConfig as O, TelemetryMetricRecord as Ot, PersistedWorkflowTokenRegistryLike as P, AnyRunnableNodeConfig as Q, RunResult as R, NodeInspectorSummaryRow as S, WebhookInvocationMatch as St, PersistedRunPolicySnapshot as T, WorkflowRunDetailDto as Tt, WorkflowExecutionRepository as U, RunSummary as V, Container as W, TestSuiteRunStatus as X, TestCaseRunStatus as Y, ChainCursor as Z, Item as _, CredentialType as _t, BinaryStorage as a, CredentialBindingKey as at, NodeActivationId as b, CredentialTypeRegistry as bt, NodeExecutionContext as c, CredentialInstanceId as ct, NodeExecutionScheduler as d, CredentialMaterialSourceKind as dt, DefinedNodeConfigInput as et, ResumeContext as f, CredentialOAuth2AuthDefinition as ft, BinaryAttachment as g, CredentialSetupStatus as gt, ActivationIdFactory as h, CredentialSessionService as ht, ToolConfig as i, CredentialBinding as it, CurrentStateExecutionRequest as j, WorkflowId as jt, RunnableNodeOutputJson as k, TelemetrySpanEventRecord as kt, NodeExecutionRequest as l, CredentialInstanceRecord as lt, WorkflowSnapshotResolver as m, CredentialSessionFactoryArgs as mt, AgentMessageConfig as n, McpServerDeclaration as nt, LiveWorkflowRepository as o, CredentialFieldSchema as ot, WorkflowRepository as p, CredentialRequirement as pt, RunEvent as q, ChatModelConfig as r, AnyCredentialType as rt, NodeActivationContinuation as s, CredentialHealth as st, AgentGuardrailConfig as t, WorkflowActivationPolicy as tt, NodeExecutionRequestHandler as u, CredentialJsonRecord as ut, Items as v, CredentialTypeDefinition as vt, ParentExecutionRef as w, WebhookTriggerResolution as wt, NodeDefinition as x, HttpMethod as xt, JsonValue as y, CredentialTypeId as yt, RunStatus as z };
1846
+ //# sourceMappingURL=ItemsInputNormalizer-CFkfNMLt.d.ts.map