@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
|
@@ -2,6 +2,7 @@ import { instanceCachingFactory, type DependencyContainer } from "../../di";
|
|
|
2
2
|
import { CoreTokens } from "../../di";
|
|
3
3
|
import { EngineExecutionLimitsPolicyFactory } from "../../policies/executionLimits/EngineExecutionLimitsPolicyFactory";
|
|
4
4
|
import {
|
|
5
|
+
ChildExecutionScopeFactory,
|
|
5
6
|
DefaultAsyncSleeper,
|
|
6
7
|
InProcessRetryRunnerFactory,
|
|
7
8
|
ItemExprResolver,
|
|
@@ -50,19 +51,25 @@ export class EngineRuntimeRegistrar {
|
|
|
50
51
|
if (!container.isRegistered(RunnableOutputBehaviorResolver, true)) {
|
|
51
52
|
container.registerSingleton(RunnableOutputBehaviorResolver, RunnableOutputBehaviorResolver);
|
|
52
53
|
}
|
|
53
|
-
container.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
container.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
container.
|
|
64
|
-
|
|
65
|
-
|
|
54
|
+
if (!container.isRegistered(ChildExecutionScopeFactory, true)) {
|
|
55
|
+
container.register(ChildExecutionScopeFactory, {
|
|
56
|
+
useFactory: instanceCachingFactory((dependencyContainer) => {
|
|
57
|
+
return new ChildExecutionScopeFactory(dependencyContainer.resolve(CoreTokens.ActivationIdFactory));
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
container.registerSingleton(EngineExecutionLimitsPolicyFactory, EngineExecutionLimitsPolicyFactory);
|
|
62
|
+
container.registerSingleton(NodeInstanceFactoryFactory, NodeInstanceFactoryFactory);
|
|
63
|
+
container.registerSingleton(DefaultAsyncSleeper, DefaultAsyncSleeper);
|
|
64
|
+
container.registerSingleton(InProcessRetryRunnerFactory, InProcessRetryRunnerFactory);
|
|
65
|
+
container.registerSingleton(NodeExecutorFactory, NodeExecutorFactory);
|
|
66
|
+
container.registerSingleton(InlineDrivingSchedulerFactory, InlineDrivingSchedulerFactory);
|
|
67
|
+
container.registerSingleton(RunIntentServiceFactory, RunIntentServiceFactory);
|
|
68
|
+
container.registerSingleton(EngineWorkflowRunnerServiceFactory, EngineWorkflowRunnerServiceFactory);
|
|
69
|
+
container.registerSingleton(
|
|
70
|
+
WorkflowRepositoryWebhookTriggerMatcherFactory,
|
|
71
|
+
WorkflowRepositoryWebhookTriggerMatcherFactory,
|
|
72
|
+
);
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
private registerExecutionLimitsPolicy(
|
|
@@ -133,7 +140,7 @@ export class EngineRuntimeRegistrar {
|
|
|
133
140
|
}
|
|
134
141
|
|
|
135
142
|
private registerEngine(container: DependencyContainer, options: EngineRuntimeRegistrationOptions | undefined): void {
|
|
136
|
-
container.
|
|
143
|
+
container.registerSingleton(EngineFactory, EngineFactory);
|
|
137
144
|
const matcherProvider = this.resolveMatcherProvider(options);
|
|
138
145
|
container.register(Engine, {
|
|
139
146
|
useFactory: instanceCachingFactory((dependencyContainer) => {
|
package/src/browser.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type {
|
|
|
9
9
|
} from "./ai/AgentConnectionNodeCollector";
|
|
10
10
|
export type { AgentNodeConfig } from "./ai/AiHost";
|
|
11
11
|
export { ConnectionNodeIdFactory } from "./workflow/definition/ConnectionNodeIdFactory";
|
|
12
|
+
export { NodeIterationIdFactory } from "./workflow/definition/NodeIterationIdFactory";
|
|
12
13
|
export * from "./contracts/credentialTypes";
|
|
13
14
|
export * from "./contracts/runtimeTypes";
|
|
14
15
|
export * from "./contracts/runFinishedAtFactory";
|
|
@@ -9,4 +9,10 @@ export class CodemationTelemetryAttributeNames {
|
|
|
9
9
|
static readonly connectionInvocationId = "codemation.connection.invocation_id";
|
|
10
10
|
static readonly toolName = "codemation.tool.name";
|
|
11
11
|
static readonly traceParentRunId = "codemation.parent.run.id";
|
|
12
|
+
/** Per-item iteration that emitted this span/metric. Set on spans recorded inside a runnable per-item loop. */
|
|
13
|
+
static readonly iterationId = "codemation.iteration.id";
|
|
14
|
+
/** Item index (0-based) of the iteration. */
|
|
15
|
+
static readonly iterationIndex = "codemation.iteration.index";
|
|
16
|
+
/** Set when this span/metric was recorded under a sub-agent triggered by an outer LLM/tool call. */
|
|
17
|
+
static readonly parentInvocationId = "codemation.parent.invocation_id";
|
|
12
18
|
}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { NodeExecutionTelemetry, TelemetryChildSpanStart, TelemetrySpanScope } from "./telemetryTypes";
|
|
1
|
+
import type { NodeExecutionTelemetry } from "./telemetryTypes";
|
|
3
2
|
import { NoOpTelemetrySpanScope } from "./NoOpTelemetrySpanScope";
|
|
4
3
|
|
|
5
4
|
export class NoOpNodeExecutionTelemetry {
|
|
6
|
-
static readonly value: NodeExecutionTelemetry =
|
|
7
|
-
...NoOpTelemetrySpanScope.value,
|
|
8
|
-
forNode(_: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry {
|
|
9
|
-
return NoOpNodeExecutionTelemetry.value;
|
|
10
|
-
},
|
|
11
|
-
startChildSpan(_: TelemetryChildSpanStart): TelemetrySpanScope {
|
|
12
|
-
return NoOpTelemetrySpanScope.value;
|
|
13
|
-
},
|
|
14
|
-
};
|
|
5
|
+
static readonly value: NodeExecutionTelemetry = NoOpTelemetrySpanScope.nodeExecutionTelemetryValue;
|
|
15
6
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { NodeActivationId, NodeId } from "./workflowTypes";
|
|
1
2
|
import type {
|
|
3
|
+
NodeExecutionTelemetry,
|
|
2
4
|
TelemetryArtifactAttachment,
|
|
3
5
|
TelemetryArtifactReference,
|
|
6
|
+
TelemetryChildSpanStart,
|
|
4
7
|
TelemetryMetricRecord,
|
|
5
8
|
TelemetrySpanEnd,
|
|
6
9
|
TelemetrySpanEventRecord,
|
|
@@ -8,15 +11,48 @@ import type {
|
|
|
8
11
|
} from "./telemetryTypes";
|
|
9
12
|
import { NoOpTelemetryArtifactReference } from "./NoOpTelemetryArtifactReference";
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Standalone no-op {@link NodeExecutionTelemetry} value used as the return for `asNodeTelemetry`.
|
|
16
|
+
*
|
|
17
|
+
* Defined here (instead of in `NoOpNodeExecutionTelemetry.ts`) so that {@link NoOpTelemetrySpanScope}
|
|
18
|
+
* can return it without importing the other module — both no-ops share this leaf.
|
|
19
|
+
*/
|
|
20
|
+
const noOpNodeExecutionTelemetry: NodeExecutionTelemetry = {
|
|
21
|
+
traceId: "00000000000000000000000000000000",
|
|
22
|
+
spanId: "0000000000000000",
|
|
23
|
+
addSpanEvent(_: TelemetrySpanEventRecord): void {},
|
|
24
|
+
recordMetric(_: TelemetryMetricRecord): void {},
|
|
25
|
+
attachArtifact(_: TelemetryArtifactAttachment): TelemetryArtifactReference {
|
|
26
|
+
return NoOpTelemetryArtifactReference.value;
|
|
27
|
+
},
|
|
28
|
+
end(_: TelemetrySpanEnd = {}): void {},
|
|
29
|
+
asNodeTelemetry(_: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry {
|
|
30
|
+
return noOpNodeExecutionTelemetry;
|
|
31
|
+
},
|
|
32
|
+
forNode(_: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry {
|
|
33
|
+
return noOpNodeExecutionTelemetry;
|
|
34
|
+
},
|
|
35
|
+
startChildSpan(_: TelemetryChildSpanStart): TelemetrySpanScope {
|
|
36
|
+
return noOpTelemetrySpanScope;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const noOpTelemetrySpanScope: TelemetrySpanScope = {
|
|
41
|
+
traceId: "00000000000000000000000000000000",
|
|
42
|
+
spanId: "0000000000000000",
|
|
43
|
+
addSpanEvent(_: TelemetrySpanEventRecord): void {},
|
|
44
|
+
recordMetric(_: TelemetryMetricRecord): void {},
|
|
45
|
+
attachArtifact(_: TelemetryArtifactAttachment): TelemetryArtifactReference {
|
|
46
|
+
return NoOpTelemetryArtifactReference.value;
|
|
47
|
+
},
|
|
48
|
+
end(_: TelemetrySpanEnd = {}): void {},
|
|
49
|
+
asNodeTelemetry(_: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry {
|
|
50
|
+
return noOpNodeExecutionTelemetry;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
11
54
|
export class NoOpTelemetrySpanScope {
|
|
12
|
-
static readonly value: TelemetrySpanScope =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
addSpanEvent(_: TelemetrySpanEventRecord): void {},
|
|
16
|
-
recordMetric(_: TelemetryMetricRecord): void {},
|
|
17
|
-
attachArtifact(_: TelemetryArtifactAttachment): TelemetryArtifactReference {
|
|
18
|
-
return NoOpTelemetryArtifactReference.value;
|
|
19
|
-
},
|
|
20
|
-
end(_: TelemetrySpanEnd = {}): void {},
|
|
21
|
-
};
|
|
55
|
+
static readonly value: TelemetrySpanScope = noOpTelemetrySpanScope;
|
|
56
|
+
/** Internal: the shared no-op {@link NodeExecutionTelemetry} that {@link NoOpNodeExecutionTelemetry} re-exposes. */
|
|
57
|
+
static readonly nodeExecutionTelemetryValue: NodeExecutionTelemetry = noOpNodeExecutionTelemetry;
|
|
22
58
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { JsonValue, NodeId } from "./workflowTypes";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One assertion emitted by an assertion-emitting node (a node whose config sets
|
|
5
|
+
* `emitsAssertions: true`). Each emitted item on `main` carries one of these as `item.json`.
|
|
6
|
+
*
|
|
7
|
+
* Pass/fail is derived from `score >= (passThreshold ?? 0.5)` — see {@link deriveAssertionPassed}.
|
|
8
|
+
* The `errored` marker is for cases where the assertion code itself threw (distinct from
|
|
9
|
+
* "the assertion was evaluated and the score was low") and is treated as a hard fail in rollups
|
|
10
|
+
* regardless of `score`.
|
|
11
|
+
*/
|
|
12
|
+
export interface AssertionResult {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/** 0..1 score. Source of truth for pass/fail (compared against `passThreshold`). */
|
|
15
|
+
readonly score: number;
|
|
16
|
+
/** 0..1 threshold for "passed". When omitted, consumers default to 0.5. */
|
|
17
|
+
readonly passThreshold?: number;
|
|
18
|
+
/** True when evaluating the assertion threw — treated as fail regardless of `score`. */
|
|
19
|
+
readonly errored?: true;
|
|
20
|
+
/** What the assertion expected. Free-form JSON; UIs render with a JSON viewer. */
|
|
21
|
+
readonly expected?: JsonValue;
|
|
22
|
+
/** What the workflow actually produced. */
|
|
23
|
+
readonly actual?: JsonValue;
|
|
24
|
+
/** Short human-readable explanation, especially for fails / errors. */
|
|
25
|
+
readonly message?: string;
|
|
26
|
+
/** Bag of supplemental fields (e.g. judge prompt, judge raw response, comparison method). */
|
|
27
|
+
readonly details?: Readonly<Record<string, JsonValue>>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Default {@link AssertionResult.passThreshold} when authors omit it. Boolean-style assertions
|
|
32
|
+
* (assertEqual / contains / etc.) emit `score: 1` or `score: 0` so this default works for them;
|
|
33
|
+
* AI-judge assertions are expected to set their own threshold.
|
|
34
|
+
*/
|
|
35
|
+
export const DEFAULT_ASSERTION_PASS_THRESHOLD = 0.5;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Derive whether an assertion result is considered "passing" using the score-based contract:
|
|
39
|
+
* `errored` always fails, otherwise `score >= (passThreshold ?? 0.5)`. This is the canonical
|
|
40
|
+
* derivation — UI and rollup code should call it rather than inlining the comparison so future
|
|
41
|
+
* tweaks (e.g. NaN handling) land in one place.
|
|
42
|
+
*/
|
|
43
|
+
export function deriveAssertionPassed(result: {
|
|
44
|
+
readonly score: number;
|
|
45
|
+
readonly passThreshold?: number;
|
|
46
|
+
readonly errored?: true;
|
|
47
|
+
}): boolean {
|
|
48
|
+
if (result.errored === true) return false;
|
|
49
|
+
const threshold = result.passThreshold ?? DEFAULT_ASSERTION_PASS_THRESHOLD;
|
|
50
|
+
return result.score >= threshold;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Provenance for a persisted {@link AssertionResult}: which node produced it and where in the
|
|
55
|
+
* per-item iteration tree it landed. Filled in by the host-side persister, not the node itself.
|
|
56
|
+
*/
|
|
57
|
+
export interface AssertionResultProvenance {
|
|
58
|
+
readonly nodeId: NodeId;
|
|
59
|
+
/** Per-item iteration id when the emitting node ran inside a per-item loop. */
|
|
60
|
+
readonly iterationId?: string;
|
|
61
|
+
/** Item index (0-based) within the activation that produced this assertion. */
|
|
62
|
+
readonly itemIndex?: number;
|
|
63
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal base types that have no dependencies on other contracts.
|
|
3
|
+
* Used by credentialTypes, workflowTypes, and other contract layers
|
|
4
|
+
* to avoid circular dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type WorkflowId = string;
|
|
8
|
+
export type NodeId = string;
|
|
9
|
+
export type OutputPortKey = string;
|
|
10
|
+
export type InputPortKey = string;
|
|
11
|
+
export type PersistedTokenId = string;
|
|
12
|
+
export type NodeConnectionName = string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a typed store for a single collection.
|
|
3
|
+
* All rows include auto-managed id, created_at, and updated_at fields.
|
|
4
|
+
*/
|
|
5
|
+
export interface CollectionStore<TRow extends Record<string, unknown> = Record<string, unknown>> {
|
|
6
|
+
/**
|
|
7
|
+
* Insert a new row. id, created_at, and updated_at are auto-populated.
|
|
8
|
+
*/
|
|
9
|
+
insert(row: TRow): Promise<TRow & { id: string; created_at: Date; updated_at: Date }>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get a single row by id.
|
|
13
|
+
*/
|
|
14
|
+
get(id: string): Promise<(TRow & { id: string; created_at: Date; updated_at: Date }) | null>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Find a single row matching the provided filter.
|
|
18
|
+
*/
|
|
19
|
+
findOne(filter: Partial<TRow>): Promise<(TRow & { id: string; created_at: Date; updated_at: Date }) | null>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* List rows with optional pagination and filtering.
|
|
23
|
+
*/
|
|
24
|
+
list(opts?: {
|
|
25
|
+
limit?: number;
|
|
26
|
+
offset?: number;
|
|
27
|
+
where?: Partial<TRow>;
|
|
28
|
+
}): Promise<{ rows: ReadonlyArray<TRow & { id: string; created_at: Date; updated_at: Date }>; total: number }>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Update a row by id with partial data.
|
|
32
|
+
*/
|
|
33
|
+
update(id: string, patch: Partial<TRow>): Promise<TRow & { id: string; created_at: Date; updated_at: Date }>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Delete a row by id. Hard delete only (no soft delete).
|
|
37
|
+
*/
|
|
38
|
+
delete(id: string): Promise<{ deleted: boolean }>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Runtime collections context: keyed by collection name.
|
|
43
|
+
*/
|
|
44
|
+
export type CollectionsContext = Readonly<Record<string, CollectionStore>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeId, WorkflowId } from "./
|
|
1
|
+
import type { NodeId, WorkflowId } from "./baseTypes";
|
|
2
2
|
|
|
3
3
|
export type CredentialTypeId = string;
|
|
4
4
|
export type CredentialInstanceId = string;
|
|
@@ -91,6 +91,28 @@ export type CredentialOAuth2AuthDefinition = Readonly<
|
|
|
91
91
|
clientIdFieldKey?: string;
|
|
92
92
|
clientSecretFieldKey?: string;
|
|
93
93
|
}
|
|
94
|
+
| {
|
|
95
|
+
kind: "oauth2";
|
|
96
|
+
/**
|
|
97
|
+
* Free-form provider identifier for telemetry, DB rows, and Better Auth provider naming.
|
|
98
|
+
* Not used for any registry lookup — URLs come from {@link authorizeUrl} / {@link tokenUrl}.
|
|
99
|
+
*/
|
|
100
|
+
providerId: string;
|
|
101
|
+
/**
|
|
102
|
+
* Authorization endpoint. May contain `{publicFieldKey}` placeholders that the runtime
|
|
103
|
+
* substitutes from the credential's resolved public config (URL-encoded).
|
|
104
|
+
* Example: `https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize`
|
|
105
|
+
*/
|
|
106
|
+
authorizeUrl: string;
|
|
107
|
+
/** Token endpoint. Same templating rules as {@link authorizeUrl}. */
|
|
108
|
+
tokenUrl: string;
|
|
109
|
+
/** Optional userinfo endpoint. Same templating rules as {@link authorizeUrl}. */
|
|
110
|
+
userInfoUrl?: string;
|
|
111
|
+
scopes: ReadonlyArray<string>;
|
|
112
|
+
scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
|
|
113
|
+
clientIdFieldKey?: string;
|
|
114
|
+
clientSecretFieldKey?: string;
|
|
115
|
+
}
|
|
94
116
|
>;
|
|
95
117
|
|
|
96
118
|
export type CredentialAuthDefinition = CredentialOAuth2AuthDefinition;
|
|
@@ -148,6 +148,30 @@ export interface WorkflowRunDetailDto {
|
|
|
148
148
|
readonly mutableState?: PersistedMutableRunState;
|
|
149
149
|
readonly slotStates: ReadonlyArray<SlotExecutionStateDto>;
|
|
150
150
|
readonly executionInstances: ReadonlyArray<ExecutionInstanceDto>;
|
|
151
|
+
readonly iterations?: ReadonlyArray<RunIterationDto>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Per-item iteration projected from connection invocations and node activations.
|
|
156
|
+
*
|
|
157
|
+
* One iteration = one item processed by an agent within an activation. Multiple invocations
|
|
158
|
+
* (LLM rounds, tool calls) belonging to the same iteration share the iterationId.
|
|
159
|
+
*/
|
|
160
|
+
export interface RunIterationDto {
|
|
161
|
+
readonly iterationId: string;
|
|
162
|
+
readonly agentNodeId: NodeId;
|
|
163
|
+
readonly activationId: NodeActivationId;
|
|
164
|
+
readonly itemIndex: number;
|
|
165
|
+
readonly itemSummary?: string;
|
|
166
|
+
readonly status: NodeExecutionStatus;
|
|
167
|
+
readonly startedAt?: string;
|
|
168
|
+
readonly finishedAt?: string;
|
|
169
|
+
readonly invocationIds: ReadonlyArray<string>;
|
|
170
|
+
readonly parentInvocationId?: string;
|
|
171
|
+
/** 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`). */
|
|
172
|
+
readonly estimatedCostMinorByCurrency?: Readonly<Record<string, number>>;
|
|
173
|
+
/** Currency scale (denominator) per currency, when present on the metric points. Joined with `estimatedCostMinorByCurrency` to format human-readable amounts. */
|
|
174
|
+
readonly estimatedCostCurrencyScaleByCurrency?: Readonly<Record<string, number>>;
|
|
151
175
|
}
|
|
152
176
|
|
|
153
177
|
export interface SlotExecutionStateDto {
|
|
@@ -178,6 +202,12 @@ export interface ExecutionInstanceDto {
|
|
|
178
202
|
readonly inputJson?: JsonValue;
|
|
179
203
|
readonly outputJson?: JsonValue;
|
|
180
204
|
readonly error?: Readonly<NodeExecutionError>;
|
|
205
|
+
/** Per-item iteration that produced this instance. Set on connectionInvocation rows produced inside per-item runnable loops. */
|
|
206
|
+
readonly iterationId?: string;
|
|
207
|
+
/** Item index (0-based) of the iteration. */
|
|
208
|
+
readonly itemIndex?: number;
|
|
209
|
+
/** Parent invocation id when this instance was emitted by a sub-agent triggered by an outer LLM/tool call. */
|
|
210
|
+
readonly parentInvocationId?: string;
|
|
181
211
|
}
|
|
182
212
|
|
|
183
213
|
export interface WorkflowDetailSelectionState {
|
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,
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
JsonValue,
|
|
8
8
|
NodeActivationId,
|
|
9
9
|
NodeId,
|
|
10
|
+
NodeIterationId,
|
|
10
11
|
NodeKind,
|
|
11
12
|
NodeOutputs,
|
|
12
13
|
OutputPortKey,
|
|
@@ -19,6 +20,23 @@ import type {
|
|
|
19
20
|
WorkflowNodeConnection,
|
|
20
21
|
} from "./workflowTypes";
|
|
21
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
|
+
|
|
22
40
|
export interface RunExecutionOptions {
|
|
23
41
|
/** Run-intent override: force the inline scheduler and bypass node-level offload decisions. */
|
|
24
42
|
localOnly?: boolean;
|
|
@@ -35,6 +53,8 @@ export interface RunExecutionOptions {
|
|
|
35
53
|
maxNodeActivations?: number;
|
|
36
54
|
/** Effective cap after engine policy merge (subworkflow nesting). */
|
|
37
55
|
maxSubworkflowDepth?: number;
|
|
56
|
+
/** Present iff started by a TestSuiteOrchestrator; propagates to subworkflow runs via {@link ParentExecutionRef.testContext}. */
|
|
57
|
+
testContext?: RunTestContext;
|
|
38
58
|
}
|
|
39
59
|
|
|
40
60
|
/** Engine-owned counters persisted with the run (worker-safe). */
|
|
@@ -154,6 +174,12 @@ export interface ConnectionInvocationRecord {
|
|
|
154
174
|
readonly startedAt?: string;
|
|
155
175
|
readonly finishedAt?: string;
|
|
156
176
|
readonly updatedAt: string;
|
|
177
|
+
/** Per-item iteration id minted by the engine when this invocation occurred inside a runnable node's per-item loop. */
|
|
178
|
+
readonly iterationId?: NodeIterationId;
|
|
179
|
+
/** Item index (0-based) of the iteration that produced this invocation. */
|
|
180
|
+
readonly itemIndex?: number;
|
|
181
|
+
/** When set, this invocation was produced inside a sub-agent triggered by the named parent invocation. */
|
|
182
|
+
readonly parentInvocationId?: ConnectionInvocationId;
|
|
157
183
|
}
|
|
158
184
|
|
|
159
185
|
/** Arguments for appending a {@link ConnectionInvocationRecord} (engine fills run/workflow ids and timestamps). */
|
|
@@ -169,6 +195,9 @@ export type ConnectionInvocationAppendArgs = Readonly<{
|
|
|
169
195
|
queuedAt?: string;
|
|
170
196
|
startedAt?: string;
|
|
171
197
|
finishedAt?: string;
|
|
198
|
+
iterationId?: NodeIterationId;
|
|
199
|
+
itemIndex?: number;
|
|
200
|
+
parentInvocationId?: ConnectionInvocationId;
|
|
172
201
|
}>;
|
|
173
202
|
|
|
174
203
|
export interface RunCurrentState {
|
|
@@ -210,6 +239,13 @@ export interface RunSummary {
|
|
|
210
239
|
workflowId: WorkflowId;
|
|
211
240
|
startedAt: string;
|
|
212
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;
|
|
213
249
|
/** ISO timestamp when the run finished (derived from node snapshots or store `updatedAt`); omit while running/pending. */
|
|
214
250
|
finishedAt?: string;
|
|
215
251
|
parent?: ParentExecutionRef;
|
|
@@ -2,14 +2,17 @@ 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,
|
|
9
|
+
ConnectionInvocationId,
|
|
8
10
|
NodeInputsByPort,
|
|
9
11
|
PersistedWorkflowSnapshot,
|
|
10
12
|
PersistedWorkflowTokenRegistryLike,
|
|
11
13
|
RunExecutionOptions,
|
|
12
14
|
RunResult,
|
|
15
|
+
RunTestContext,
|
|
13
16
|
WorkflowExecutionRepository,
|
|
14
17
|
} from "./runTypes";
|
|
15
18
|
import type { WorkflowActivationPolicy } from "./workflowActivationPolicy";
|
|
@@ -25,6 +28,7 @@ import type {
|
|
|
25
28
|
NodeActivationId,
|
|
26
29
|
NodeConfigBase,
|
|
27
30
|
NodeId,
|
|
31
|
+
NodeIterationId,
|
|
28
32
|
NodeOutputs,
|
|
29
33
|
RunnableNodeConfig,
|
|
30
34
|
OutputPortKey,
|
|
@@ -154,6 +158,21 @@ export interface ExecutionContext {
|
|
|
154
158
|
telemetry: ExecutionTelemetry;
|
|
155
159
|
binary: ExecutionBinaryService;
|
|
156
160
|
getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
|
|
161
|
+
/** Per-item iteration id, set by {@link NodeExecutor} on the ctx passed into runnable `execute`. */
|
|
162
|
+
iterationId?: NodeIterationId;
|
|
163
|
+
/** Item index (0-based) within the current activation's batch; set alongside {@link iterationId}. */
|
|
164
|
+
itemIndex?: number;
|
|
165
|
+
/** When set, this ctx is executing inside a sub-agent triggered by the named parent invocation. */
|
|
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;
|
|
157
176
|
}
|
|
158
177
|
|
|
159
178
|
export interface ExecutionContextFactory {
|
|
@@ -169,6 +188,7 @@ export interface ExecutionContextFactory {
|
|
|
169
188
|
nodeState?: NodeExecutionStatePublisher;
|
|
170
189
|
telemetry?: ExecutionTelemetry;
|
|
171
190
|
getCredential<TSession = unknown>(slotKey: string): Promise<TSession>;
|
|
191
|
+
testContext?: RunTestContext;
|
|
172
192
|
}): ExecutionContext;
|
|
173
193
|
}
|
|
174
194
|
|
|
@@ -180,6 +200,24 @@ export interface NodeExecutionContext<TConfig extends NodeConfigBase = NodeConfi
|
|
|
180
200
|
binary: NodeBinaryAttachmentService;
|
|
181
201
|
}
|
|
182
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
|
+
|
|
183
221
|
export interface TriggerSetupContext<
|
|
184
222
|
TConfig extends TriggerNodeConfig<any, any> = TriggerNodeConfig<any, any>,
|
|
185
223
|
TSetupState extends JsonValue | undefined = TriggerNodeSetupState<TConfig>,
|
|
@@ -189,6 +227,8 @@ export interface TriggerSetupContext<
|
|
|
189
227
|
previousState: TSetupState;
|
|
190
228
|
registerCleanup(cleanup: TriggerCleanupHandle): void;
|
|
191
229
|
emit(items: Items): Promise<void>;
|
|
230
|
+
/** Generic polling-trigger surface. Pre-binds trigger id, emit, and registerCleanup. */
|
|
231
|
+
readonly polling: PollingTriggerHandle;
|
|
192
232
|
}
|
|
193
233
|
|
|
194
234
|
export interface TriggerTestItemsContext<
|
|
@@ -431,4 +471,6 @@ export interface EngineDeps {
|
|
|
431
471
|
workflowPolicyRuntimeDefaults?: WorkflowPolicyRuntimeDefaults;
|
|
432
472
|
/** When set, logs inactive-workflow skips at boot and trigger start/stop on activation changes. */
|
|
433
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;
|
|
434
476
|
}
|
|
@@ -73,6 +73,14 @@ export interface TelemetrySpanScope extends TelemetryScope {
|
|
|
73
73
|
readonly traceId: string;
|
|
74
74
|
readonly spanId: string;
|
|
75
75
|
end(args?: TelemetrySpanEnd): Promise<void> | void;
|
|
76
|
+
/**
|
|
77
|
+
* Lift this span into a {@link NodeExecutionTelemetry} scoped to a different (nodeId, activationId).
|
|
78
|
+
* Children created via the returned telemetry's `startChildSpan` get this span as their parent.
|
|
79
|
+
*
|
|
80
|
+
* Used at the sub-agent boundary so that nested runtime telemetry parents under the agent.tool.call
|
|
81
|
+
* span instead of the orchestrator's node-level span.
|
|
82
|
+
*/
|
|
83
|
+
asNodeTelemetry(args: Readonly<{ nodeId: NodeId; activationId: NodeActivationId }>): NodeExecutionTelemetry;
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
export interface NodeExecutionTelemetry extends ExecutionTelemetry, TelemetrySpanScope {
|
|
@@ -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
|
+
}
|