@codemation/core 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +390 -0
- package/dist/{EngineRuntimeRegistration.types-BP6tsaNP.d.ts → EngineRuntimeRegistration.types-D1fyApMI.d.ts} +2 -2
- package/dist/{EngineWorkflowRunnerService-DzOCa1BW.d.cts → EngineRuntimeRegistration.types-pB3FnzqR.d.cts} +17 -17
- package/dist/{InMemoryRunDataFactory-1iz7_SnO.d.cts → InMemoryRunDataFactory-Xw7v4-sj.d.cts} +31 -29
- package/dist/InMemoryRunEventBusRegistry-VM3OWnHo.cjs +47 -0
- package/dist/InMemoryRunEventBusRegistry-VM3OWnHo.cjs.map +1 -0
- package/dist/InMemoryRunEventBusRegistry-sM4z4n_i.js +41 -0
- package/dist/InMemoryRunEventBusRegistry-sM4z4n_i.js.map +1 -0
- package/dist/{RunIntentService-BqhmdoA1.d.ts → RunIntentService-BE9CAkbf.d.ts} +966 -471
- package/dist/{RunIntentService-S-1lW-gS.d.cts → RunIntentService-siBSjaaY.d.cts} +859 -493
- package/dist/bootstrap/index.cjs +5 -2
- package/dist/bootstrap/index.d.cts +212 -135
- package/dist/bootstrap/index.d.ts +4 -4
- package/dist/bootstrap/index.js +3 -3
- package/dist/{bootstrap-BaN6hZ5I.cjs → bootstrap-Cm5ruQxx.cjs} +263 -12
- package/dist/bootstrap-Cm5ruQxx.cjs.map +1 -0
- package/dist/bootstrap-D3r505ko.js +454 -0
- package/dist/bootstrap-D3r505ko.js.map +1 -0
- package/dist/{index-CVs9rVhl.d.ts → index-DeLl1Tne.d.ts} +632 -230
- package/dist/index.cjs +323 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +544 -91
- package/dist/index.d.ts +3 -3
- package/dist/index.js +299 -166
- package/dist/index.js.map +1 -1
- package/dist/{runtime-DUW6tIJ1.js → runtime-BGNbRnqs.js} +934 -75
- package/dist/runtime-BGNbRnqs.js.map +1 -0
- package/dist/{runtime-Dvo2ru5A.cjs → runtime-DKXJwTNv.cjs} +1028 -73
- package/dist/runtime-DKXJwTNv.cjs.map +1 -0
- package/dist/testing.cjs +5 -5
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +4 -4
- package/dist/testing.js.map +1 -1
- package/package.json +7 -2
- package/src/ai/AiHost.ts +42 -14
- package/src/authoring/DefinedCollectionRegistry.ts +17 -0
- package/src/authoring/defineCollection.types.ts +181 -0
- package/src/authoring/definePollingTrigger.types.ts +396 -0
- package/src/authoring/definePollingTriggerInternals.ts +74 -0
- package/src/authoring/index.ts +19 -0
- package/src/bootstrap/index.ts +9 -0
- package/src/bootstrap/runtime/EngineRuntimeRegistrar.ts +21 -14
- package/src/browser.ts +1 -0
- package/src/contracts/CodemationTelemetryAttributeNames.ts +6 -0
- package/src/contracts/NoOpNodeExecutionTelemetry.ts +2 -11
- package/src/contracts/NoOpTelemetrySpanScope.ts +46 -10
- package/src/contracts/assertionTypes.ts +63 -0
- package/src/contracts/baseTypes.ts +12 -0
- package/src/contracts/collectionTypes.ts +44 -0
- package/src/contracts/credentialTypes.ts +23 -1
- package/src/contracts/executionPersistenceContracts.ts +30 -0
- package/src/contracts/index.ts +4 -0
- package/src/contracts/runTypes.ts +37 -1
- package/src/contracts/runtimeTypes.ts +42 -0
- package/src/contracts/telemetryTypes.ts +8 -0
- package/src/contracts/testTriggerTypes.ts +66 -0
- package/src/contracts/workflowTypes.ts +36 -7
- package/src/contracts.ts +59 -0
- package/src/events/ConnectionInvocationEventPublisher.ts +46 -0
- package/src/events/index.ts +1 -0
- package/src/events/runEvents.ts +74 -0
- package/src/execution/ChildExecutionScopeFactory.ts +55 -0
- package/src/execution/DefaultExecutionContextFactory.ts +6 -0
- package/src/execution/ExecutionTelemetryCostTrackingDecoratorFactory.ts +18 -0
- package/src/execution/NodeExecutor.ts +10 -2
- package/src/execution/NodeInstanceFactory.ts +13 -1
- package/src/execution/NodeInstantiationError.ts +16 -0
- package/src/execution/NodeRunStateWriter.ts +7 -0
- package/src/execution/NodeRunStateWriterFactory.ts +7 -0
- package/src/execution/WorkflowRunExecutionContextFactory.ts +3 -0
- package/src/execution/index.ts +2 -0
- package/src/index.ts +8 -0
- package/src/orchestration/AbortControllerFactory.ts +9 -0
- package/src/orchestration/NodeExecutionRequestHandlerService.ts +1 -0
- package/src/orchestration/RunContinuationService.ts +3 -0
- package/src/orchestration/RunStartService.ts +122 -3
- package/src/orchestration/TestSuiteOrchestrator.ts +350 -0
- package/src/orchestration/TestSuiteRunIdFactory.ts +11 -0
- package/src/orchestration/TriggerRuntimeService.ts +34 -7
- package/src/orchestration/index.ts +9 -0
- package/src/runtime/EngineFactory.ts +12 -0
- package/src/testing/WorkflowTestKitNodeRegistrationContextFactory.ts +1 -3
- package/src/triggers/polling/PollingTriggerDedupWindow.ts +23 -0
- package/src/triggers/polling/PollingTriggerLogger.ts +18 -0
- package/src/triggers/polling/PollingTriggerRuntime.ts +122 -0
- package/src/triggers/polling/index.ts +5 -0
- package/src/types/index.ts +12 -9
- package/src/workflow/definition/NodeIterationIdFactory.ts +26 -0
- package/src/workflow/dsl/NodeIdSlugifier.ts +18 -0
- package/src/workflow/dsl/WorkflowBuilder.ts +71 -3
- package/src/workflow/dsl/WorkflowDefinitionError.ts +15 -0
- package/src/workflow/index.ts +3 -0
- package/dist/InMemoryRunEventBusRegistry-B0_C4OnP.cjs +0 -262
- package/dist/InMemoryRunEventBusRegistry-B0_C4OnP.cjs.map +0 -1
- package/dist/InMemoryRunEventBusRegistry-C2U83Hmv.js +0 -238
- package/dist/InMemoryRunEventBusRegistry-C2U83Hmv.js.map +0 -1
- package/dist/bootstrap-BaN6hZ5I.cjs.map +0 -1
- package/dist/bootstrap-d_BMaDT4.js +0 -221
- package/dist/bootstrap-d_BMaDT4.js.map +0 -1
- package/dist/runtime-DUW6tIJ1.js.map +0 -1
- package/dist/runtime-Dvo2ru5A.cjs.map +0 -1
|
@@ -3,13 +3,18 @@ import type { ZodType } from "zod";
|
|
|
3
3
|
import type { TypeToken } from "../di";
|
|
4
4
|
import type { CredentialRequirement } from "./credentialTypes";
|
|
5
5
|
import type { RetryPolicySpec } from "./retryPolicySpec.types";
|
|
6
|
+
import type { InputPortKey, NodeConnectionName, NodeId, OutputPortKey, WorkflowId } from "./baseTypes";
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
InputPortKey,
|
|
10
|
+
NodeConnectionName,
|
|
11
|
+
NodeId,
|
|
12
|
+
OutputPortKey,
|
|
13
|
+
PersistedTokenId,
|
|
14
|
+
WorkflowId,
|
|
15
|
+
} from "./baseTypes";
|
|
6
16
|
|
|
7
|
-
export type WorkflowId = string;
|
|
8
|
-
export type NodeId = string;
|
|
9
17
|
export type NodeIdRef<TJson = unknown> = NodeId & Readonly<{ __codemationNodeJson?: TJson }>;
|
|
10
|
-
export type OutputPortKey = string;
|
|
11
|
-
export type InputPortKey = string;
|
|
12
|
-
export type PersistedTokenId = string;
|
|
13
18
|
|
|
14
19
|
export type NodeKind = "trigger" | "node";
|
|
15
20
|
export type JsonPrimitive = string | number | boolean | null;
|
|
@@ -26,8 +31,6 @@ export interface Edge {
|
|
|
26
31
|
to: { nodeId: NodeId; input: InputPortKey };
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
export type NodeConnectionName = string;
|
|
30
|
-
|
|
31
34
|
/**
|
|
32
35
|
* Named connection from a parent node to child nodes that exist in {@link WorkflowDefinition.nodes}
|
|
33
36
|
* but are not traversed by the main execution graph. Parents are commonly executable nodes, but may
|
|
@@ -91,6 +94,14 @@ export interface NodeConfigBase {
|
|
|
91
94
|
readonly declaredOutputPorts?: ReadonlyArray<OutputPortKey>;
|
|
92
95
|
readonly declaredInputPorts?: ReadonlyArray<InputPortKey>;
|
|
93
96
|
getCredentialRequirements?(): ReadonlyArray<CredentialRequirement>;
|
|
97
|
+
/**
|
|
98
|
+
* Marker: this node emits {@link import("./assertionTypes").AssertionResult}-shaped items on its
|
|
99
|
+
* `main` port. The TestSuiteOrchestrator (and host-side TestAssertionPersister) listen for
|
|
100
|
+
* `nodeCompleted` events from nodes with this flag set, and persist their output items as
|
|
101
|
+
* TestAssertion records (only when the run carries a `testContext`). Set on assertion node
|
|
102
|
+
* configs (e.g. `AssertionNodeConfig`, `StringEqualsAssertionNodeConfig`).
|
|
103
|
+
*/
|
|
104
|
+
readonly emitsAssertions?: true;
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
export declare const runnableNodeInputType: unique symbol;
|
|
@@ -127,6 +138,12 @@ export interface TriggerNodeConfig<
|
|
|
127
138
|
readonly kind: "trigger";
|
|
128
139
|
readonly [triggerNodeOutputType]?: TOutputJson;
|
|
129
140
|
readonly [triggerNodeSetupStateType]?: TSetupState;
|
|
141
|
+
/**
|
|
142
|
+
* Distinguishes triggers driven by the live activation policy (webhooks, cron, polling) from
|
|
143
|
+
* triggers driven only by the {@link TestSuiteOrchestrator}. `WorkflowActivation` skips
|
|
144
|
+
* `"test"` triggers; the orchestrator skips `"live"` triggers. Defaults to `"live"` when omitted.
|
|
145
|
+
*/
|
|
146
|
+
readonly triggerKind?: "live" | "test";
|
|
130
147
|
}
|
|
131
148
|
|
|
132
149
|
export type RunnableNodeInputJson<TConfig extends RunnableNodeConfig<any, any>> =
|
|
@@ -194,6 +211,12 @@ export type NodeOutputs = Partial<Record<OutputPortKey, Items>>;
|
|
|
194
211
|
|
|
195
212
|
export type RunId = string;
|
|
196
213
|
export type NodeActivationId = string;
|
|
214
|
+
/**
|
|
215
|
+
* One per-item iteration of a runnable node's execute loop. Refines `NodeActivationId` for
|
|
216
|
+
* per-item connection invocations and telemetry. Undefined when the executing node is a batch
|
|
217
|
+
* node or trigger that does not iterate items.
|
|
218
|
+
*/
|
|
219
|
+
export type NodeIterationId = string;
|
|
197
220
|
|
|
198
221
|
export interface ParentExecutionRef {
|
|
199
222
|
runId: RunId;
|
|
@@ -205,6 +228,12 @@ export interface ParentExecutionRef {
|
|
|
205
228
|
engineMaxNodeActivations?: number;
|
|
206
229
|
/** Effective max subworkflow depth from the parent run (propagated to child policy merge). */
|
|
207
230
|
engineMaxSubworkflowDepth?: number;
|
|
231
|
+
/**
|
|
232
|
+
* Test-suite linkage inherited by the child subworkflow run. Set by whichever node
|
|
233
|
+
* spawns the subworkflow when its own `ctx.testContext` is present, so assertions
|
|
234
|
+
* emitted inside a subworkflow land under the correct parent test case.
|
|
235
|
+
*/
|
|
236
|
+
testContext?: import("./runTypes").RunTestContext;
|
|
208
237
|
}
|
|
209
238
|
|
|
210
239
|
export interface RunDataSnapshot {
|
package/src/contracts.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Pure-type-only re-exports. Use this for type-only consumers that should not drag in runtime DSL or factory code.
|
|
2
|
+
// This subpath prevents unnecessary compile-graph bloat for packages that only need types like NodeId, Items, etc.
|
|
3
|
+
|
|
4
|
+
export type * from "./contracts/baseTypes";
|
|
5
|
+
export type * from "./contracts/assertionTypes";
|
|
6
|
+
// assertionTypes also exports a runtime helper for deriving pass/fail from a score+threshold.
|
|
7
|
+
// We keep the type-only re-export above and surface the helper explicitly here so UI consumers
|
|
8
|
+
// (next-host) don't need to re-implement the comparison.
|
|
9
|
+
export { deriveAssertionPassed, DEFAULT_ASSERTION_PASS_THRESHOLD } from "./contracts/assertionTypes";
|
|
10
|
+
export type * from "./contracts/params";
|
|
11
|
+
export type * from "./contracts/retryPolicySpec.types";
|
|
12
|
+
export type * from "./contracts/CostCatalogContract";
|
|
13
|
+
export type * from "./contracts/executionPersistenceContracts";
|
|
14
|
+
export type * from "./contracts/runtimeTypes";
|
|
15
|
+
export type * from "./contracts/telemetryTypes";
|
|
16
|
+
export type * from "./contracts/testTriggerTypes";
|
|
17
|
+
export type * from "./contracts/runTypes";
|
|
18
|
+
export type * from "./contracts/webhookTypes";
|
|
19
|
+
export type * from "./contracts/workflowTypes";
|
|
20
|
+
|
|
21
|
+
// credentialTypes mixes types (Credential* interfaces) with runtime (CredentialUnboundError class).
|
|
22
|
+
// Export type-only subset for pure type consumers.
|
|
23
|
+
export type {
|
|
24
|
+
CredentialTypeId,
|
|
25
|
+
CredentialInstanceId,
|
|
26
|
+
CredentialMaterialSourceKind,
|
|
27
|
+
CredentialSetupStatus,
|
|
28
|
+
CredentialHealthStatus,
|
|
29
|
+
CredentialFieldSchema,
|
|
30
|
+
CredentialRequirement,
|
|
31
|
+
CredentialBindingKey,
|
|
32
|
+
CredentialBinding,
|
|
33
|
+
CredentialHealth,
|
|
34
|
+
OAuth2ProviderFromPublicConfig,
|
|
35
|
+
CredentialOAuth2ScopesFromPublicConfig,
|
|
36
|
+
CredentialOAuth2AuthDefinition,
|
|
37
|
+
CredentialAuthDefinition,
|
|
38
|
+
CredentialAdvancedSectionPresentation,
|
|
39
|
+
CredentialTypeDefinition,
|
|
40
|
+
CredentialJsonRecord,
|
|
41
|
+
CredentialInstanceRecord,
|
|
42
|
+
CredentialSessionFactoryArgs,
|
|
43
|
+
CredentialSessionFactory,
|
|
44
|
+
CredentialHealthTester,
|
|
45
|
+
CredentialType,
|
|
46
|
+
AnyCredentialType,
|
|
47
|
+
CredentialSessionService,
|
|
48
|
+
CredentialTypeRegistry,
|
|
49
|
+
} from "./contracts/credentialTypes";
|
|
50
|
+
|
|
51
|
+
// CostTrackingTelemetryContract mixes types with const runtime values (metric/attribute names).
|
|
52
|
+
// Export type-only subset for pure type consumers.
|
|
53
|
+
export type {
|
|
54
|
+
CostTrackingComponent,
|
|
55
|
+
CostTrackingUsageRecord,
|
|
56
|
+
CostTrackingPriceQuote,
|
|
57
|
+
CostTrackingTelemetry,
|
|
58
|
+
CostTrackingTelemetryFactory,
|
|
59
|
+
} from "./contracts/CostTrackingTelemetryContract";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ConnectionInvocationRecord } from "../contracts/runTypes";
|
|
2
|
+
import type { ParentExecutionRef } from "../types";
|
|
3
|
+
import type { RunEventBus } from "./runEvents";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Publishes per-invocation lifecycle records onto the run {@link RunEventBus}.
|
|
7
|
+
*
|
|
8
|
+
* Surgical, per-invocation events let the UI update the right-side inspector
|
|
9
|
+
* timeline as each LLM round / tool call transitions through `running` → `completed`
|
|
10
|
+
* (or `failed`) without depending on a coarse `runSaved` poll.
|
|
11
|
+
*/
|
|
12
|
+
export class ConnectionInvocationEventPublisher {
|
|
13
|
+
constructor(
|
|
14
|
+
private readonly eventBus: RunEventBus | undefined,
|
|
15
|
+
private readonly parent: ParentExecutionRef | undefined,
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
async publish(record: ConnectionInvocationRecord): Promise<void> {
|
|
19
|
+
if (!this.eventBus) return;
|
|
20
|
+
const kind = this.kindFor(record);
|
|
21
|
+
if (!kind) return;
|
|
22
|
+
await this.eventBus.publish({
|
|
23
|
+
kind,
|
|
24
|
+
runId: record.runId,
|
|
25
|
+
workflowId: record.workflowId,
|
|
26
|
+
parent: this.parent,
|
|
27
|
+
at: record.updatedAt,
|
|
28
|
+
record,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private kindFor(
|
|
33
|
+
record: ConnectionInvocationRecord,
|
|
34
|
+
): "connectionInvocationStarted" | "connectionInvocationCompleted" | "connectionInvocationFailed" | undefined {
|
|
35
|
+
if (record.status === "running" || record.status === "queued") {
|
|
36
|
+
return "connectionInvocationStarted";
|
|
37
|
+
}
|
|
38
|
+
if (record.status === "completed") {
|
|
39
|
+
return "connectionInvocationCompleted";
|
|
40
|
+
}
|
|
41
|
+
if (record.status === "failed") {
|
|
42
|
+
return "connectionInvocationFailed";
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/events/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { ConnectionInvocationEventPublisher } from "./ConnectionInvocationEventPublisher";
|
|
1
2
|
export { NodeEventPublisher } from "./NodeEventPublisher";
|
|
2
3
|
export { InMemoryRunEventBus } from "./InMemoryRunEventBusRegistry";
|
|
3
4
|
export { EventPublishingWorkflowExecutionRepository } from "./EventPublishingWorkflowExecutionRepository";
|
package/src/events/runEvents.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import type { TestSuiteRunId } from "../contracts/testTriggerTypes";
|
|
2
|
+
import type { ConnectionInvocationRecord } from "../contracts/runTypes";
|
|
1
3
|
import type { NodeExecutionSnapshot, ParentExecutionRef, PersistedRunState, RunId, WorkflowId } from "../types";
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Outcome of a single test case (one workflow run dispatched by the test-suite orchestrator).
|
|
7
|
+
* - `running`: workflow still in flight
|
|
8
|
+
* - `succeeded`: workflow completed AND all assertions passed (or no assertions)
|
|
9
|
+
* - `failed`: workflow failed OR (workflow completed but ≥1 assertion failed)
|
|
10
|
+
* - `errored` / `cancelled`: workflow itself errored or was cancelled
|
|
11
|
+
*/
|
|
12
|
+
export type TestCaseRunStatus = "running" | "succeeded" | "failed" | "errored" | "cancelled";
|
|
13
|
+
/** Aggregate outcome of a TestSuiteRun. */
|
|
14
|
+
export type TestSuiteRunStatus = "succeeded" | "failed" | "partial" | "errored" | "cancelled";
|
|
15
|
+
|
|
3
16
|
export type RunEvent =
|
|
4
17
|
| Readonly<{ kind: "runCreated"; runId: RunId; workflowId: WorkflowId; parent?: ParentExecutionRef; at: string }>
|
|
5
18
|
| Readonly<{
|
|
@@ -41,6 +54,67 @@ export type RunEvent =
|
|
|
41
54
|
parent?: ParentExecutionRef;
|
|
42
55
|
at: string;
|
|
43
56
|
snapshot: NodeExecutionSnapshot;
|
|
57
|
+
}>
|
|
58
|
+
| Readonly<{
|
|
59
|
+
kind: "connectionInvocationStarted";
|
|
60
|
+
runId: RunId;
|
|
61
|
+
workflowId: WorkflowId;
|
|
62
|
+
parent?: ParentExecutionRef;
|
|
63
|
+
at: string;
|
|
64
|
+
record: ConnectionInvocationRecord;
|
|
65
|
+
}>
|
|
66
|
+
| Readonly<{
|
|
67
|
+
kind: "connectionInvocationCompleted";
|
|
68
|
+
runId: RunId;
|
|
69
|
+
workflowId: WorkflowId;
|
|
70
|
+
parent?: ParentExecutionRef;
|
|
71
|
+
at: string;
|
|
72
|
+
record: ConnectionInvocationRecord;
|
|
73
|
+
}>
|
|
74
|
+
| Readonly<{
|
|
75
|
+
kind: "connectionInvocationFailed";
|
|
76
|
+
runId: RunId;
|
|
77
|
+
workflowId: WorkflowId;
|
|
78
|
+
parent?: ParentExecutionRef;
|
|
79
|
+
at: string;
|
|
80
|
+
record: ConnectionInvocationRecord;
|
|
81
|
+
}>
|
|
82
|
+
| Readonly<{
|
|
83
|
+
kind: "testSuiteStarted";
|
|
84
|
+
testSuiteRunId: TestSuiteRunId;
|
|
85
|
+
workflowId: WorkflowId;
|
|
86
|
+
triggerNodeId: string;
|
|
87
|
+
triggerNodeName?: string;
|
|
88
|
+
concurrency: number;
|
|
89
|
+
at: string;
|
|
90
|
+
}>
|
|
91
|
+
| Readonly<{
|
|
92
|
+
kind: "testSuiteFinished";
|
|
93
|
+
testSuiteRunId: TestSuiteRunId;
|
|
94
|
+
workflowId: WorkflowId;
|
|
95
|
+
status: TestSuiteRunStatus;
|
|
96
|
+
totalCases: number;
|
|
97
|
+
passedCases: number;
|
|
98
|
+
failedCases: number;
|
|
99
|
+
at: string;
|
|
100
|
+
}>
|
|
101
|
+
| Readonly<{
|
|
102
|
+
kind: "testCaseStarted";
|
|
103
|
+
testSuiteRunId: TestSuiteRunId;
|
|
104
|
+
testCaseIndex: number;
|
|
105
|
+
runId: RunId;
|
|
106
|
+
workflowId: WorkflowId;
|
|
107
|
+
testCaseLabel?: string;
|
|
108
|
+
at: string;
|
|
109
|
+
}>
|
|
110
|
+
| Readonly<{
|
|
111
|
+
kind: "testCaseCompleted";
|
|
112
|
+
testSuiteRunId: TestSuiteRunId;
|
|
113
|
+
testCaseIndex: number;
|
|
114
|
+
runId: RunId;
|
|
115
|
+
workflowId: WorkflowId;
|
|
116
|
+
status: TestCaseRunStatus;
|
|
117
|
+
at: string;
|
|
44
118
|
}>;
|
|
45
119
|
|
|
46
120
|
export interface RunEventSubscription {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActivationIdFactory,
|
|
3
|
+
ConnectionInvocationId,
|
|
4
|
+
NodeExecutionContext,
|
|
5
|
+
NodeId,
|
|
6
|
+
RunnableNodeConfig,
|
|
7
|
+
TelemetrySpanScope,
|
|
8
|
+
} from "../types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builds a re-rooted child execution context for sub-agent (and other deeply-nested) invocations.
|
|
12
|
+
*
|
|
13
|
+
* At the orchestrator's `agent.tool.call` boundary the inner runtime needs a ctx whose:
|
|
14
|
+
* - `nodeId` is the tool's connection node id (so inner LLM/tool connection ids derive correctly),
|
|
15
|
+
* - `activationId` is fresh (so its connection-invocation rows are uniquely identifiable),
|
|
16
|
+
* - `telemetry` parents children under the tool-call span (not the orchestrator's node span),
|
|
17
|
+
* - `binary` is scoped to the new (nodeId, activationId),
|
|
18
|
+
* - `parentInvocationId` points back to the tool-call invocation for downstream lineage.
|
|
19
|
+
*
|
|
20
|
+
* Registered via factory in {@link EngineRuntimeRegistrar} so constructors stay free of parameter
|
|
21
|
+
* decorators (Next/SWC and coverage tooling cannot parse them on in-repo sources).
|
|
22
|
+
*/
|
|
23
|
+
export class ChildExecutionScopeFactory {
|
|
24
|
+
constructor(private readonly activationIdFactory: ActivationIdFactory) {}
|
|
25
|
+
|
|
26
|
+
forSubAgent<TConfig extends RunnableNodeConfig<any, any>>(
|
|
27
|
+
args: Readonly<{
|
|
28
|
+
parentCtx: NodeExecutionContext<TConfig>;
|
|
29
|
+
childNodeId: NodeId;
|
|
30
|
+
childConfig: TConfig;
|
|
31
|
+
parentInvocationId: ConnectionInvocationId;
|
|
32
|
+
parentSpan: TelemetrySpanScope;
|
|
33
|
+
}>,
|
|
34
|
+
): NodeExecutionContext<TConfig> {
|
|
35
|
+
const childActivationId = this.activationIdFactory.makeActivationId();
|
|
36
|
+
const childTelemetry = args.parentSpan.asNodeTelemetry({
|
|
37
|
+
nodeId: args.childNodeId,
|
|
38
|
+
activationId: childActivationId,
|
|
39
|
+
});
|
|
40
|
+
const childBinary = args.parentCtx.binary.forNode({
|
|
41
|
+
nodeId: args.childNodeId,
|
|
42
|
+
activationId: childActivationId,
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
...args.parentCtx,
|
|
46
|
+
nodeId: args.childNodeId,
|
|
47
|
+
activationId: childActivationId,
|
|
48
|
+
config: args.childConfig,
|
|
49
|
+
telemetry: childTelemetry,
|
|
50
|
+
binary: childBinary,
|
|
51
|
+
parentInvocationId: args.parentInvocationId,
|
|
52
|
+
iterationId: undefined,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
BinaryStorage,
|
|
3
|
+
CollectionsContext,
|
|
3
4
|
CostTrackingTelemetryFactory,
|
|
4
5
|
ExecutionContext,
|
|
5
6
|
ExecutionContextFactory,
|
|
@@ -8,6 +9,7 @@ import type {
|
|
|
8
9
|
ParentExecutionRef,
|
|
9
10
|
RunDataSnapshot,
|
|
10
11
|
RunId,
|
|
12
|
+
RunTestContext,
|
|
11
13
|
WorkflowId,
|
|
12
14
|
} from "../types";
|
|
13
15
|
import { NoOpCostTrackingTelemetryFactory, NoOpExecutionTelemetryFactory } from "../types";
|
|
@@ -26,6 +28,7 @@ export class DefaultExecutionContextFactory implements ExecutionContextFactory {
|
|
|
26
28
|
private readonly telemetryFactory: ExecutionTelemetryFactory = new NoOpExecutionTelemetryFactory(),
|
|
27
29
|
private readonly costTrackingFactory: CostTrackingTelemetryFactory = new NoOpCostTrackingTelemetryFactory(),
|
|
28
30
|
private readonly currentDate: () => Date = () => new Date(),
|
|
31
|
+
private readonly collections?: CollectionsContext,
|
|
29
32
|
) {}
|
|
30
33
|
|
|
31
34
|
create(args: {
|
|
@@ -40,6 +43,7 @@ export class DefaultExecutionContextFactory implements ExecutionContextFactory {
|
|
|
40
43
|
nodeState?: NodeExecutionStatePublisher;
|
|
41
44
|
telemetry?: ExecutionContext["telemetry"];
|
|
42
45
|
getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
|
|
46
|
+
testContext?: RunTestContext;
|
|
43
47
|
}): ExecutionContext {
|
|
44
48
|
const baseTelemetry =
|
|
45
49
|
args.telemetry ??
|
|
@@ -66,6 +70,8 @@ export class DefaultExecutionContextFactory implements ExecutionContextFactory {
|
|
|
66
70
|
telemetry,
|
|
67
71
|
binary: new DefaultExecutionBinaryService(this.binaryStorage, args.workflowId, args.runId, this.currentDate),
|
|
68
72
|
getCredential: args.getCredential,
|
|
73
|
+
testContext: args.testContext,
|
|
74
|
+
collections: this.collections,
|
|
69
75
|
};
|
|
70
76
|
}
|
|
71
77
|
}
|
|
@@ -62,6 +62,15 @@ export class ExecutionTelemetryCostTrackingDecoratorFactory {
|
|
|
62
62
|
costTracking: args.costTracking.forScope(nodeTelemetry),
|
|
63
63
|
});
|
|
64
64
|
},
|
|
65
|
+
asNodeTelemetry: (
|
|
66
|
+
rescope: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>,
|
|
67
|
+
): NodeExecutionTelemetry => {
|
|
68
|
+
const nodeTelemetry = args.telemetry.asNodeTelemetry(rescope);
|
|
69
|
+
return this.decorateNodeExecutionTelemetry({
|
|
70
|
+
telemetry: nodeTelemetry,
|
|
71
|
+
costTracking: args.costTracking.forScope(nodeTelemetry),
|
|
72
|
+
});
|
|
73
|
+
},
|
|
65
74
|
};
|
|
66
75
|
}
|
|
67
76
|
|
|
@@ -79,6 +88,15 @@ export class ExecutionTelemetryCostTrackingDecoratorFactory {
|
|
|
79
88
|
artifact: TelemetryArtifactAttachment,
|
|
80
89
|
): Promise<TelemetryArtifactReference> | TelemetryArtifactReference => args.scope.attachArtifact(artifact),
|
|
81
90
|
end: (endArgs?: TelemetrySpanEnd) => args.scope.end(endArgs),
|
|
91
|
+
asNodeTelemetry: (
|
|
92
|
+
rescope: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>,
|
|
93
|
+
): NodeExecutionTelemetry => {
|
|
94
|
+
const nodeTelemetry = args.scope.asNodeTelemetry(rescope);
|
|
95
|
+
return this.decorateNodeExecutionTelemetry({
|
|
96
|
+
telemetry: nodeTelemetry,
|
|
97
|
+
costTracking: args.costTracking.forScope(nodeTelemetry),
|
|
98
|
+
});
|
|
99
|
+
},
|
|
82
100
|
};
|
|
83
101
|
}
|
|
84
102
|
}
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
TriggerNode,
|
|
14
14
|
WorkflowNodeInstanceFactory,
|
|
15
15
|
} from "../types";
|
|
16
|
+
import { NodeIterationIdFactory } from "../workflow/definition/NodeIterationIdFactory";
|
|
16
17
|
|
|
17
18
|
import { FanInMergeByOriginMerger } from "./FanInMergeByOriginMerger";
|
|
18
19
|
import { ItemExprResolver } from "./ItemExprResolver";
|
|
@@ -158,13 +159,20 @@ export class NodeExecutor {
|
|
|
158
159
|
const parsed = inputSchema.parse(item.json);
|
|
159
160
|
const runnableCtx = request.ctx as NodeExecutionContext<RunnableNodeConfig>;
|
|
160
161
|
const resolvedCtx = await this.itemExprResolver.resolveConfigForItem(runnableCtx, item, i, inputBatch);
|
|
161
|
-
const
|
|
162
|
+
const baseCtx = this.pickExecutionContext(runnableCtx, resolvedCtx);
|
|
163
|
+
// Mint a per-item iteration id and stamp it (with the item index) onto the ctx so connection
|
|
164
|
+
// invocations and telemetry written from inside `node.execute` carry the per-item identity.
|
|
165
|
+
const iterationCtx = {
|
|
166
|
+
...baseCtx,
|
|
167
|
+
iterationId: NodeIterationIdFactory.create(),
|
|
168
|
+
itemIndex: i,
|
|
169
|
+
} as NodeExecutionContext<RunnableNodeConfig>;
|
|
162
170
|
const args: RunnableNodeExecuteArgs = {
|
|
163
171
|
input: parsed,
|
|
164
172
|
item,
|
|
165
173
|
itemIndex: i,
|
|
166
174
|
items: inputBatch,
|
|
167
|
-
ctx,
|
|
175
|
+
ctx: iterationCtx,
|
|
168
176
|
};
|
|
169
177
|
const raw = await Promise.resolve(node.execute(args));
|
|
170
178
|
const normalized = this.outputNormalizer.normalizeExecuteResult({
|
|
@@ -4,6 +4,7 @@ import type { NodeId, NodeResolver, WorkflowDefinition, WorkflowNodeInstanceFact
|
|
|
4
4
|
import { MissingRuntimeNode, MissingRuntimeTrigger } from "../workflowSnapshots";
|
|
5
5
|
import { MissingRuntimeNodeToken } from "../workflowSnapshots/MissingRuntimeNodeToken";
|
|
6
6
|
import { MissingRuntimeTriggerToken } from "../workflowSnapshots/MissingRuntimeTriggerToken";
|
|
7
|
+
import { NodeInstantiationError } from "./NodeInstantiationError";
|
|
7
8
|
|
|
8
9
|
export class NodeInstanceFactory implements WorkflowNodeInstanceFactory {
|
|
9
10
|
constructor(private readonly nodeResolver: NodeResolver) {}
|
|
@@ -11,7 +12,18 @@ export class NodeInstanceFactory implements WorkflowNodeInstanceFactory {
|
|
|
11
12
|
createNodes(workflow: WorkflowDefinition): Map<NodeId, unknown> {
|
|
12
13
|
const nodeInstances = new Map<NodeId, unknown>();
|
|
13
14
|
for (const definition of workflow.nodes) {
|
|
14
|
-
|
|
15
|
+
try {
|
|
16
|
+
nodeInstances.set(definition.id, this.createNode(definition));
|
|
17
|
+
} catch (err) {
|
|
18
|
+
if (err instanceof NodeInstantiationError) {
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
throw new NodeInstantiationError(
|
|
22
|
+
definition.id,
|
|
23
|
+
typeof definition.type === "function" ? definition.type.name : String(definition.type),
|
|
24
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
15
27
|
}
|
|
16
28
|
return nodeInstances;
|
|
17
29
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NodeId } from "../types";
|
|
2
|
+
|
|
3
|
+
export class NodeInstantiationError extends Error {
|
|
4
|
+
readonly name = "NodeInstantiationError";
|
|
5
|
+
readonly originalError: Error;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
readonly nodeId: NodeId,
|
|
9
|
+
readonly nodeType: string,
|
|
10
|
+
originalError: Error,
|
|
11
|
+
) {
|
|
12
|
+
super(`Failed to instantiate node "${nodeId}" (type ${nodeType}): ${originalError.message}`);
|
|
13
|
+
this.originalError = originalError;
|
|
14
|
+
this.stack = originalError.stack;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -28,6 +28,7 @@ export class NodeRunStateWriter implements NodeExecutionStatePublisher {
|
|
|
28
28
|
kind: "nodeQueued" | "nodeStarted" | "nodeCompleted" | "nodeFailed",
|
|
29
29
|
snapshot: NodeExecutionSnapshot,
|
|
30
30
|
) => Promise<void>,
|
|
31
|
+
private readonly publishConnectionInvocationEvent?: (record: ConnectionInvocationRecord) => Promise<void>,
|
|
31
32
|
) {}
|
|
32
33
|
|
|
33
34
|
markQueued(args: {
|
|
@@ -148,11 +149,17 @@ export class NodeRunStateWriter implements NodeExecutionStatePublisher {
|
|
|
148
149
|
startedAt: args.startedAt,
|
|
149
150
|
finishedAt: args.finishedAt,
|
|
150
151
|
updatedAt,
|
|
152
|
+
iterationId: args.iterationId,
|
|
153
|
+
itemIndex: args.itemIndex,
|
|
154
|
+
parentInvocationId: args.parentInvocationId,
|
|
151
155
|
};
|
|
152
156
|
await this.workflowExecutionRepository.save({
|
|
153
157
|
...state,
|
|
154
158
|
connectionInvocations: [...(state.connectionInvocations ?? []), record],
|
|
155
159
|
});
|
|
160
|
+
if (this.publishConnectionInvocationEvent) {
|
|
161
|
+
await this.publishConnectionInvocationEvent(record);
|
|
162
|
+
}
|
|
156
163
|
});
|
|
157
164
|
}
|
|
158
165
|
|
|
@@ -6,16 +6,20 @@ import type {
|
|
|
6
6
|
WorkflowId,
|
|
7
7
|
} from "../types";
|
|
8
8
|
|
|
9
|
+
import { ConnectionInvocationEventPublisher } from "../events/ConnectionInvocationEventPublisher";
|
|
9
10
|
import { NodeEventPublisher } from "../events/NodeEventPublisher";
|
|
11
|
+
import type { RunEventBus } from "../events/runEvents";
|
|
10
12
|
import { NodeRunStateWriter } from "./NodeRunStateWriter";
|
|
11
13
|
|
|
12
14
|
export class NodeRunStateWriterFactory {
|
|
13
15
|
constructor(
|
|
14
16
|
private readonly workflowExecutionRepository: WorkflowExecutionRepository,
|
|
15
17
|
private readonly nodeEventPublisher: NodeEventPublisher,
|
|
18
|
+
private readonly eventBus?: RunEventBus,
|
|
16
19
|
) {}
|
|
17
20
|
|
|
18
21
|
create(runId: RunId, workflowId: WorkflowId, parent: ParentExecutionRef | undefined): NodeExecutionStatePublisher {
|
|
22
|
+
const connectionInvocationEventPublisher = new ConnectionInvocationEventPublisher(this.eventBus, parent);
|
|
19
23
|
return new NodeRunStateWriter(
|
|
20
24
|
this.workflowExecutionRepository,
|
|
21
25
|
runId,
|
|
@@ -24,6 +28,9 @@ export class NodeRunStateWriterFactory {
|
|
|
24
28
|
async (kind, snapshot) => {
|
|
25
29
|
await this.nodeEventPublisher.publish(kind, snapshot);
|
|
26
30
|
},
|
|
31
|
+
async (record) => {
|
|
32
|
+
await connectionInvocationEventPublisher.publish(record);
|
|
33
|
+
},
|
|
27
34
|
);
|
|
28
35
|
}
|
|
29
36
|
}
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
ParentExecutionRef,
|
|
6
6
|
RunDataFactory,
|
|
7
7
|
RunId,
|
|
8
|
+
RunTestContext,
|
|
8
9
|
WorkflowId,
|
|
9
10
|
} from "../types";
|
|
10
11
|
|
|
@@ -30,6 +31,7 @@ export class WorkflowRunExecutionContextFactory {
|
|
|
30
31
|
engineMaxSubworkflowDepth: number;
|
|
31
32
|
data: ReturnType<RunDataFactory["create"]>;
|
|
32
33
|
nodeState?: NodeExecutionStatePublisher;
|
|
34
|
+
testContext?: RunTestContext;
|
|
33
35
|
}) {
|
|
34
36
|
return this.executionContextFactory.create({
|
|
35
37
|
runId: args.runId,
|
|
@@ -42,6 +44,7 @@ export class WorkflowRunExecutionContextFactory {
|
|
|
42
44
|
data: args.data,
|
|
43
45
|
nodeState: args.nodeState,
|
|
44
46
|
getCredential: this.credentialResolverFactory.create(args.workflowId, args.nodeId),
|
|
47
|
+
testContext: args.testContext,
|
|
45
48
|
});
|
|
46
49
|
}
|
|
47
50
|
}
|
package/src/execution/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { ActivationEnqueueService } from "./ActivationEnqueueService";
|
|
2
|
+
export { ChildExecutionScopeFactory } from "./ChildExecutionScopeFactory";
|
|
2
3
|
export { NodeActivationRequestInputPreparer } from "./NodeActivationRequestInputPreparer";
|
|
3
4
|
export { NodeInputContractError } from "./NodeInputContractError";
|
|
5
|
+
export { NodeInstantiationError } from "./NodeInstantiationError";
|
|
4
6
|
export { CredentialResolverFactory } from "./CredentialResolverFactory";
|
|
5
7
|
export { DefaultAsyncSleeper } from "./DefaultAsyncSleeper";
|
|
6
8
|
export { DefaultExecutionContextFactory } from "./DefaultExecutionContextFactory";
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from "./runtime-types/runtimeTypeDecorators.types";
|
|
|
14
14
|
export * from "./serialization/ItemsInputNormalizer";
|
|
15
15
|
export { DefaultExecutionBinaryService, UnavailableBinaryStorage } from "./binaries";
|
|
16
16
|
export {
|
|
17
|
+
ChildExecutionScopeFactory,
|
|
17
18
|
CredentialResolverFactory,
|
|
18
19
|
DefaultAsyncSleeper,
|
|
19
20
|
DefaultExecutionContextFactory,
|
|
@@ -26,3 +27,10 @@ export { EngineExecutionLimitsPolicy, type EngineExecutionLimitsPolicyConfig } f
|
|
|
26
27
|
export { InMemoryBinaryStorage, InMemoryRunDataFactory } from "./runStorage";
|
|
27
28
|
export { InMemoryLiveWorkflowRepository, RunIntentService } from "./runtime";
|
|
28
29
|
export * from "./types";
|
|
30
|
+
export { PollingTriggerRuntime, PollingTriggerDedupWindow, NoOpPollingTriggerLogger } from "./triggers/polling";
|
|
31
|
+
export type {
|
|
32
|
+
PollingTriggerLogger,
|
|
33
|
+
PollingRunCycleArgs,
|
|
34
|
+
PollingRunCycleResult,
|
|
35
|
+
PollingTriggerStartArgs,
|
|
36
|
+
} from "./triggers/polling";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mints fresh {@link AbortController}s. Injected (rather than direct `new`) to honor the
|
|
3
|
+
* codebase's no-direct-construction rule and to give tests a seam for substituting a fake.
|
|
4
|
+
*/
|
|
5
|
+
export class AbortControllerFactory {
|
|
6
|
+
create(): AbortController {
|
|
7
|
+
return new AbortController();
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -77,6 +77,7 @@ export class NodeExecutionRequestHandlerService implements NodeExecutionRequestH
|
|
|
77
77
|
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
78
78
|
data,
|
|
79
79
|
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, resolvedParent),
|
|
80
|
+
testContext: state.executionOptions?.testContext,
|
|
80
81
|
});
|
|
81
82
|
|
|
82
83
|
const inputsByPort = pendingExecution.inputsByPort;
|
|
@@ -141,6 +141,7 @@ export class RunContinuationService {
|
|
|
141
141
|
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
142
142
|
data,
|
|
143
143
|
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, state.parent),
|
|
144
|
+
testContext: state.executionOptions?.testContext,
|
|
144
145
|
});
|
|
145
146
|
|
|
146
147
|
data.setOutputs(args.nodeId, args.outputs);
|
|
@@ -755,6 +756,7 @@ export class RunContinuationService {
|
|
|
755
756
|
engineMaxSubworkflowDepth: webhookLimits.engineMaxSubworkflowDepth,
|
|
756
757
|
data,
|
|
757
758
|
nodeState: this.nodeStatePublisherFactory.create(args.state.runId, args.state.workflowId, args.state.parent),
|
|
759
|
+
testContext: args.state.executionOptions?.testContext,
|
|
758
760
|
});
|
|
759
761
|
const request = this.nodeActivationRequestComposer.createFromPlannedActivation({
|
|
760
762
|
next,
|
|
@@ -859,6 +861,7 @@ export class RunContinuationService {
|
|
|
859
861
|
engineMaxSubworkflowDepth: limits.engineMaxSubworkflowDepth,
|
|
860
862
|
data,
|
|
861
863
|
nodeState: this.nodeStatePublisherFactory.create(state.runId, state.workflowId, state.parent),
|
|
864
|
+
testContext: state.executionOptions?.testContext,
|
|
862
865
|
});
|
|
863
866
|
const activationId = pendingExecution.activationId;
|
|
864
867
|
return {
|