@codemation/core 1.0.1 → 2.0.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 +293 -0
- package/dist/{EngineRuntimeRegistration.types-kxQA5NLt.d.ts → EngineRuntimeRegistration.types-D1fyApMI.d.ts} +2 -2
- package/dist/{EngineWorkflowRunnerService-Ba2AvBnL.d.cts → EngineRuntimeRegistration.types-pB3FnzqR.d.cts} +17 -17
- package/dist/{InMemoryRunDataFactory-Ou4tQUOS.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-dteLjNiT.d.ts → RunIntentService-BE9CAkbf.d.ts} +602 -213
- package/dist/{RunIntentService-Dyh_dH0k.d.cts → RunIntentService-siBSjaaY.d.cts} +430 -125
- 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-Cko6udwL.cjs → bootstrap-Cm5ruQxx.cjs} +253 -3
- package/dist/bootstrap-Cm5ruQxx.cjs.map +1 -0
- package/dist/{bootstrap-CL68rqWg.js → bootstrap-D3r505ko.js} +236 -4
- package/dist/bootstrap-D3r505ko.js.map +1 -0
- package/dist/{index-CyfGTfU1.d.ts → index-DeLl1Tne.d.ts} +574 -242
- package/dist/index.cjs +328 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +441 -103
- package/dist/index.d.ts +3 -3
- package/dist/index.js +305 -163
- package/dist/index.js.map +1 -1
- package/dist/{runtime-284ok0cm.js → runtime-BGNbRnqs.js} +764 -75
- package/dist/runtime-BGNbRnqs.js.map +1 -0
- package/dist/{runtime-B3Og-_St.cjs → runtime-DKXJwTNv.cjs} +841 -80
- package/dist/runtime-DKXJwTNv.cjs.map +1 -0
- package/dist/testing.cjs +4 -4
- 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 +3 -3
- package/package.json +7 -2
- 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 +5 -1
- 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/index.ts +4 -0
- package/src/contracts/runTypes.ts +27 -1
- package/src/contracts/runtimeTypes.ts +34 -0
- package/src/contracts/testTriggerTypes.ts +66 -0
- package/src/contracts/workflowTypes.ts +30 -7
- package/src/contracts.ts +59 -0
- package/src/events/runEvents.ts +49 -0
- package/src/execution/ChildExecutionScopeFactory.ts +4 -7
- package/src/execution/DefaultExecutionContextFactory.ts +6 -0
- package/src/execution/NodeInstanceFactory.ts +13 -1
- package/src/execution/NodeInstantiationError.ts +16 -0
- package/src/execution/WorkflowRunExecutionContextFactory.ts +3 -0
- package/src/execution/index.ts +1 -0
- package/src/index.ts +7 -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 +114 -2
- 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 +11 -0
- 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/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 +2 -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-CL68rqWg.js.map +0 -1
- package/dist/bootstrap-Cko6udwL.cjs.map +0 -1
- package/dist/runtime-284ok0cm.js.map +0 -1
- package/dist/runtime-B3Og-_St.cjs.map +0 -1
package/src/contracts/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export * from "./baseTypes";
|
|
2
|
+
export * from "./assertionTypes";
|
|
3
|
+
export * from "./collectionTypes";
|
|
1
4
|
export * from "./credentialTypes";
|
|
2
5
|
export * from "./emitPorts";
|
|
3
6
|
export * from "./executionPersistenceContracts";
|
|
@@ -6,6 +9,7 @@ export * from "./params";
|
|
|
6
9
|
export * from "./itemExpr";
|
|
7
10
|
export * from "./runtimeTypes";
|
|
8
11
|
export * from "./telemetryTypes";
|
|
12
|
+
export * from "./testTriggerTypes";
|
|
9
13
|
export * from "./runFinishedAtFactory";
|
|
10
14
|
export * from "./runTypes";
|
|
11
15
|
export * from "./webhookTypes";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TypeToken } from "../di";
|
|
2
|
-
import type { RunEventBus } from "../events/runEvents";
|
|
2
|
+
import type { RunEventBus, TestCaseRunStatus } from "../events/runEvents";
|
|
3
3
|
import type {
|
|
4
4
|
Edge,
|
|
5
5
|
InputPortKey,
|
|
@@ -20,6 +20,23 @@ import type {
|
|
|
20
20
|
WorkflowNodeConnection,
|
|
21
21
|
} from "./workflowTypes";
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Test-suite linkage for a run. When set, this run was started by a TestSuiteOrchestrator
|
|
25
|
+
* as one test case inside a TestSuiteRun. The `IsTestRun` node and host-side persisters key
|
|
26
|
+
* off the presence of this field. Subworkflow runs inherit it from their parent run.
|
|
27
|
+
*/
|
|
28
|
+
export interface RunTestContext {
|
|
29
|
+
readonly testSuiteRunId: string;
|
|
30
|
+
readonly testCaseIndex: number;
|
|
31
|
+
/**
|
|
32
|
+
* Optional human-friendly label for this test case (e.g. an email subject when fixtures
|
|
33
|
+
* are loaded from a mailbox). Resolved per item by `TestTrigger.caseLabel(item)` if set,
|
|
34
|
+
* persisted on `Run.test_case_label` so the Tests-tab tree-table can show "RFQ for batch 14"
|
|
35
|
+
* instead of "run_1777755971399_bbb86beac1396".
|
|
36
|
+
*/
|
|
37
|
+
readonly testCaseLabel?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
export interface RunExecutionOptions {
|
|
24
41
|
/** Run-intent override: force the inline scheduler and bypass node-level offload decisions. */
|
|
25
42
|
localOnly?: boolean;
|
|
@@ -36,6 +53,8 @@ export interface RunExecutionOptions {
|
|
|
36
53
|
maxNodeActivations?: number;
|
|
37
54
|
/** Effective cap after engine policy merge (subworkflow nesting). */
|
|
38
55
|
maxSubworkflowDepth?: number;
|
|
56
|
+
/** Present iff started by a TestSuiteOrchestrator; propagates to subworkflow runs via {@link ParentExecutionRef.testContext}. */
|
|
57
|
+
testContext?: RunTestContext;
|
|
39
58
|
}
|
|
40
59
|
|
|
41
60
|
/** Engine-owned counters persisted with the run (worker-safe). */
|
|
@@ -220,6 +239,13 @@ export interface RunSummary {
|
|
|
220
239
|
workflowId: WorkflowId;
|
|
221
240
|
startedAt: string;
|
|
222
241
|
status: RunStatus;
|
|
242
|
+
/**
|
|
243
|
+
* Test-case status for runs dispatched as part of a TestSuiteRun. Carries the
|
|
244
|
+
* assertion-rollup-corrected outcome the test orchestrator persists onto the row, so the
|
|
245
|
+
* executions list can show "failed" for a run whose workflow completed cleanly but whose
|
|
246
|
+
* assertions caught regressions. Absent for non-test runs and legacy rows.
|
|
247
|
+
*/
|
|
248
|
+
testCaseStatus?: TestCaseRunStatus;
|
|
223
249
|
/** ISO timestamp when the run finished (derived from node snapshots or store `updatedAt`); omit while running/pending. */
|
|
224
250
|
finishedAt?: string;
|
|
225
251
|
parent?: ParentExecutionRef;
|
|
@@ -2,6 +2,7 @@ import type { ReadableStream as BinaryReadableStream } from "node:stream/web";
|
|
|
2
2
|
import type { TypeToken } from "../di";
|
|
3
3
|
import type { RunEventBus } from "../events/runEvents";
|
|
4
4
|
import type { CredentialSessionService } from "./credentialTypes";
|
|
5
|
+
import type { CollectionsContext } from "./collectionTypes";
|
|
5
6
|
import type { ExecutionTelemetry, ExecutionTelemetryFactory, NodeExecutionTelemetry } from "./telemetryTypes";
|
|
6
7
|
import type {
|
|
7
8
|
ConnectionInvocationAppendArgs,
|
|
@@ -11,6 +12,7 @@ import type {
|
|
|
11
12
|
PersistedWorkflowTokenRegistryLike,
|
|
12
13
|
RunExecutionOptions,
|
|
13
14
|
RunResult,
|
|
15
|
+
RunTestContext,
|
|
14
16
|
WorkflowExecutionRepository,
|
|
15
17
|
} from "./runTypes";
|
|
16
18
|
import type { WorkflowActivationPolicy } from "./workflowActivationPolicy";
|
|
@@ -162,6 +164,15 @@ export interface ExecutionContext {
|
|
|
162
164
|
itemIndex?: number;
|
|
163
165
|
/** When set, this ctx is executing inside a sub-agent triggered by the named parent invocation. */
|
|
164
166
|
parentInvocationId?: ConnectionInvocationId;
|
|
167
|
+
/**
|
|
168
|
+
* Present iff the run was started by a TestSuiteOrchestrator. The {@link IsTestRunNode}
|
|
169
|
+
* branches on this; assertion-emitting nodes use it to decide whether to record results.
|
|
170
|
+
*/
|
|
171
|
+
testContext?: RunTestContext;
|
|
172
|
+
/**
|
|
173
|
+
* Collections registered in the codemation config, keyed by collection name.
|
|
174
|
+
*/
|
|
175
|
+
readonly collections?: CollectionsContext;
|
|
165
176
|
}
|
|
166
177
|
|
|
167
178
|
export interface ExecutionContextFactory {
|
|
@@ -177,6 +188,7 @@ export interface ExecutionContextFactory {
|
|
|
177
188
|
nodeState?: NodeExecutionStatePublisher;
|
|
178
189
|
telemetry?: ExecutionTelemetry;
|
|
179
190
|
getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
|
|
191
|
+
testContext?: RunTestContext;
|
|
180
192
|
}): ExecutionContext;
|
|
181
193
|
}
|
|
182
194
|
|
|
@@ -188,6 +200,24 @@ export interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfi
|
|
|
188
200
|
binary: NodeBinaryAttachmentService;
|
|
189
201
|
}
|
|
190
202
|
|
|
203
|
+
export interface PollingTriggerHandle {
|
|
204
|
+
/**
|
|
205
|
+
* Start the polling loop. The runtime registers its own cleanup handle so callers do not need to
|
|
206
|
+
* call {@link TriggerSetupContext.registerCleanup} for the loop.
|
|
207
|
+
* @returns The state returned by the first cycle (or `undefined` when the overlap guard fired).
|
|
208
|
+
*/
|
|
209
|
+
start<TState, TItem>(args: {
|
|
210
|
+
intervalMs: number;
|
|
211
|
+
seedState?: TState;
|
|
212
|
+
runCycle: (cycleCtx: {
|
|
213
|
+
previousState: TState | undefined;
|
|
214
|
+
signal: AbortSignal;
|
|
215
|
+
}) => Promise<{ items: Items<TItem>; nextState: TState }>;
|
|
216
|
+
}): Promise<TState | undefined>;
|
|
217
|
+
/** Convenience dedup-window helper. */
|
|
218
|
+
readonly dedup: import("../triggers/polling/PollingTriggerDedupWindow").PollingTriggerDedupWindow;
|
|
219
|
+
}
|
|
220
|
+
|
|
191
221
|
export interface TriggerSetupContext<
|
|
192
222
|
TConfig extends TriggerNodeConfig<any, any> = TriggerNodeConfig<any, any>,
|
|
193
223
|
TSetupState extends JsonValue | undefined = TriggerNodeSetupState<TConfig>,
|
|
@@ -197,6 +227,8 @@ export interface TriggerSetupContext<
|
|
|
197
227
|
previousState: TSetupState;
|
|
198
228
|
registerCleanup(cleanup: TriggerCleanupHandle): void;
|
|
199
229
|
emit(items: Items): Promise<void>;
|
|
230
|
+
/** Generic polling-trigger surface. Pre-binds trigger id, emit, and registerCleanup. */
|
|
231
|
+
readonly polling: PollingTriggerHandle;
|
|
200
232
|
}
|
|
201
233
|
|
|
202
234
|
export interface TriggerTestItemsContext<
|
|
@@ -439,4 +471,6 @@ export interface EngineDeps {
|
|
|
439
471
|
workflowPolicyRuntimeDefaults?: WorkflowPolicyRuntimeDefaults;
|
|
440
472
|
/** When set, logs inactive-workflow skips at boot and trigger start/stop on activation changes. */
|
|
441
473
|
triggerRuntimeDiagnostics?: TriggerRuntimeDiagnostics;
|
|
474
|
+
/** When set, the polling-trigger runtime uses this logger for cycle info/debug/error. */
|
|
475
|
+
pollingTriggerLogger?: import("../triggers/polling/PollingTriggerLogger").PollingTriggerLogger;
|
|
442
476
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Item, NodeId, WorkflowId } from "./workflowTypes";
|
|
2
|
+
import type { TriggerNodeConfig } from "./workflowTypes";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Identifier minted by the host (or in-memory test runner) for one execution of a test suite.
|
|
6
|
+
* One TestSuiteRun produces N child workflow runs, one per item yielded by `generateItems`.
|
|
7
|
+
*/
|
|
8
|
+
export type TestSuiteRunId = string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Setup context passed to a {@link TestTriggerNodeConfig.generateItems} callback. Distinct from
|
|
12
|
+
* {@link import("./runtimeTypes").TriggerSetupContext} on purpose: test triggers are not
|
|
13
|
+
* activated by the live trigger lifecycle (webhooks, cron, polling) and never call `emit` —
|
|
14
|
+
* the orchestrator pulls from the iterable they return and dispatches one run per item.
|
|
15
|
+
*/
|
|
16
|
+
export interface TestTriggerSetupContext<
|
|
17
|
+
TConfig extends TestTriggerNodeConfig<unknown> = TestTriggerNodeConfig<unknown>,
|
|
18
|
+
> {
|
|
19
|
+
readonly workflowId: WorkflowId;
|
|
20
|
+
readonly nodeId: NodeId;
|
|
21
|
+
readonly config: TConfig;
|
|
22
|
+
readonly testSuiteRunId: TestSuiteRunId;
|
|
23
|
+
/**
|
|
24
|
+
* Resolves a credential session for a slot declared on this trigger's
|
|
25
|
+
* {@link import("./workflowTypes").NodeConfigBase.getCredentialRequirements}. Same contract as
|
|
26
|
+
* {@link import("./runtimeTypes").ExecutionContext.getCredential}.
|
|
27
|
+
*/
|
|
28
|
+
getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
|
|
29
|
+
/** AbortSignal raised when the suite is cancelled — long-running pulls should bail out. */
|
|
30
|
+
readonly signal: AbortSignal;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A trigger config that emits **test cases**. Each item yielded by {@link generateItems}
|
|
35
|
+
* becomes one workflow run (with `executionOptions.testContext` set), so 10 yielded items
|
|
36
|
+
* → 10 runs marked under the same TestSuiteRun.
|
|
37
|
+
*
|
|
38
|
+
* The trigger is otherwise a normal {@link TriggerNodeConfig} (so the canvas treats it like
|
|
39
|
+
* any other trigger), but its `triggerKind` is `"test"` so the live activation policy skips it.
|
|
40
|
+
*/
|
|
41
|
+
export interface TestTriggerNodeConfig<TOutputJson = unknown> extends TriggerNodeConfig<TOutputJson, undefined> {
|
|
42
|
+
readonly triggerKind: "test";
|
|
43
|
+
/**
|
|
44
|
+
* Author-supplied async iterable of items, evaluated lazily. Implementations may fetch from
|
|
45
|
+
* credentialed APIs, read fixture files, or yield hard-coded items. The orchestrator iterates
|
|
46
|
+
* and dispatches one run per item, with concurrency capped by {@link concurrency} (default 4).
|
|
47
|
+
*/
|
|
48
|
+
generateItems(ctx: TestTriggerSetupContext<TestTriggerNodeConfig<TOutputJson>>): AsyncIterable<Item<TOutputJson>>;
|
|
49
|
+
/** Per-suite-run cap on simultaneously-executing test cases. Default: 4. */
|
|
50
|
+
readonly concurrency?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Free-form description of where the test cases come from — surfaced in the node properties
|
|
53
|
+
* panel and the suite-detail header so authors revisiting the workflow six months later
|
|
54
|
+
* remember which mailbox / folder / fixture file the cases originate from.
|
|
55
|
+
*
|
|
56
|
+
* Example: `"All emails in the Gmail label \"test/triage-fixtures\" — 14 messages as of 2026-05-03."`
|
|
57
|
+
*/
|
|
58
|
+
readonly description?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Resolves a human-readable label for one yielded test case (e.g. email subject). The
|
|
61
|
+
* orchestrator calls this once per yielded item, persists the result on the run, and the
|
|
62
|
+
* Tests-tab UI uses it to render the case row instead of the opaque runId. Return
|
|
63
|
+
* `undefined` to fall back to "Case #N".
|
|
64
|
+
*/
|
|
65
|
+
caseLabel?(item: Item<TOutputJson>): string | undefined;
|
|
66
|
+
}
|
|
@@ -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>> =
|
|
@@ -211,6 +228,12 @@ export interface ParentExecutionRef {
|
|
|
211
228
|
engineMaxNodeActivations?: number;
|
|
212
229
|
/** Effective max subworkflow depth from the parent run (propagated to child policy merge). */
|
|
213
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;
|
|
214
237
|
}
|
|
215
238
|
|
|
216
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";
|
package/src/events/runEvents.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
import type { TestSuiteRunId } from "../contracts/testTriggerTypes";
|
|
1
2
|
import type { ConnectionInvocationRecord } from "../contracts/runTypes";
|
|
2
3
|
import type { NodeExecutionSnapshot, ParentExecutionRef, PersistedRunState, RunId, WorkflowId } from "../types";
|
|
3
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
|
+
|
|
4
16
|
export type RunEvent =
|
|
5
17
|
| Readonly<{ kind: "runCreated"; runId: RunId; workflowId: WorkflowId; parent?: ParentExecutionRef; at: string }>
|
|
6
18
|
| Readonly<{
|
|
@@ -66,6 +78,43 @@ export type RunEvent =
|
|
|
66
78
|
parent?: ParentExecutionRef;
|
|
67
79
|
at: string;
|
|
68
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;
|
|
69
118
|
}>;
|
|
70
119
|
|
|
71
120
|
export interface RunEventSubscription {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { CoreTokens } from "../di/CoreTokens";
|
|
2
|
-
import { inject, injectable } from "../di";
|
|
3
1
|
import type {
|
|
4
2
|
ActivationIdFactory,
|
|
5
3
|
ConnectionInvocationId,
|
|
@@ -18,13 +16,12 @@ import type {
|
|
|
18
16
|
* - `telemetry` parents children under the tool-call span (not the orchestrator's node span),
|
|
19
17
|
* - `binary` is scoped to the new (nodeId, activationId),
|
|
20
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).
|
|
21
22
|
*/
|
|
22
|
-
@injectable()
|
|
23
23
|
export class ChildExecutionScopeFactory {
|
|
24
|
-
constructor(
|
|
25
|
-
@inject(CoreTokens.ActivationIdFactory)
|
|
26
|
-
private readonly activationIdFactory: ActivationIdFactory,
|
|
27
|
-
) {}
|
|
24
|
+
constructor(private readonly activationIdFactory: ActivationIdFactory) {}
|
|
28
25
|
|
|
29
26
|
forSubAgent<TConfig extends RunnableNodeConfig<any, any>>(
|
|
30
27
|
args: Readonly<{
|
|
@@ -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
|
}
|
|
@@ -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
|
+
}
|
|
@@ -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
|
@@ -2,6 +2,7 @@ export { ActivationEnqueueService } from "./ActivationEnqueueService";
|
|
|
2
2
|
export { ChildExecutionScopeFactory } from "./ChildExecutionScopeFactory";
|
|
3
3
|
export { NodeActivationRequestInputPreparer } from "./NodeActivationRequestInputPreparer";
|
|
4
4
|
export { NodeInputContractError } from "./NodeInputContractError";
|
|
5
|
+
export { NodeInstantiationError } from "./NodeInstantiationError";
|
|
5
6
|
export { CredentialResolverFactory } from "./CredentialResolverFactory";
|
|
6
7
|
export { DefaultAsyncSleeper } from "./DefaultAsyncSleeper";
|
|
7
8
|
export { DefaultExecutionContextFactory } from "./DefaultExecutionContextFactory";
|
package/src/index.ts
CHANGED
|
@@ -27,3 +27,10 @@ export { EngineExecutionLimitsPolicy, type EngineExecutionLimitsPolicyConfig } f
|
|
|
27
27
|
export { InMemoryBinaryStorage, InMemoryRunDataFactory } from "./runStorage";
|
|
28
28
|
export { InMemoryLiveWorkflowRepository, RunIntentService } from "./runtime";
|
|
29
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 {
|