@codemation/core 1.0.0 → 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 +312 -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-BfZE19lK.cjs → bootstrap-Cm5ruQxx.cjs} +253 -2
- package/dist/bootstrap-Cm5ruQxx.cjs.map +1 -0
- package/dist/{bootstrap-jqh1kCNI.js → bootstrap-D3r505ko.js} +236 -3
- package/dist/bootstrap-D3r505ko.js.map +1 -0
- package/dist/{index-CGs3Hnoz.d.ts → index-DeLl1Tne.d.ts} +599 -219
- package/dist/index.cjs +323 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +511 -80
- package/dist/index.d.ts +3 -3
- package/dist/index.js +299 -166
- package/dist/index.js.map +1 -1
- package/dist/{runtime-u6O644ST.js → runtime-BGNbRnqs.js} +933 -74
- package/dist/runtime-BGNbRnqs.js.map +1 -0
- package/dist/{runtime-DWKfb0BI.cjs → runtime-DKXJwTNv.cjs} +1027 -72
- 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/ai/AiHost.ts +9 -0
- 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 +8 -0
- 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 +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 +12 -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/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-BfZE19lK.cjs.map +0 -1
- package/dist/bootstrap-jqh1kCNI.js.map +0 -1
- package/dist/runtime-DWKfb0BI.cjs.map +0 -1
- package/dist/runtime-u6O644ST.js.map +0 -1
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* definePollingTrigger — declarative helper for authoring polling triggers.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the ergonomics of `defineNode` / `defineRestNode` / `defineCredential`.
|
|
5
|
+
* Plugin authors supply a `poll` function plus metadata; the helper synthesises the
|
|
6
|
+
* two internal classes (`DefinedPollingTriggerRuntime` + `DefinedPollingTriggerConfig`)
|
|
7
|
+
* that the engine's trigger machinery requires. Internal classes, DI annotations, and
|
|
8
|
+
* `PollingTriggerRuntime` wiring are hidden from the plugin-author surface entirely.
|
|
9
|
+
*/
|
|
10
|
+
import type {
|
|
11
|
+
Items,
|
|
12
|
+
JsonValue,
|
|
13
|
+
NodeExecutionContext,
|
|
14
|
+
NodeOutputs,
|
|
15
|
+
TestableTriggerNode,
|
|
16
|
+
TriggerNodeConfig,
|
|
17
|
+
TriggerSetupContext,
|
|
18
|
+
TriggerTestItemsContext,
|
|
19
|
+
TypeToken,
|
|
20
|
+
} from "..";
|
|
21
|
+
import type { CredentialJsonRecord, CredentialRequirement } from "../contracts/credentialTypes";
|
|
22
|
+
import type { DefinedNodeCredentialAccessors, DefinedNodeCredentialBindings } from "./defineNode.types";
|
|
23
|
+
import { node as persistedNode } from "../runtime-types/runtimeTypeDecorators.types";
|
|
24
|
+
import {
|
|
25
|
+
definedNodeCredentialRequirementFactory,
|
|
26
|
+
definedNodeCredentialAccessorFactory,
|
|
27
|
+
} from "./definePollingTriggerInternals";
|
|
28
|
+
import type { ZodType } from "zod";
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Public types
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
type MaybePromise<TValue> = TValue | Promise<TValue>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Context passed into the `poll` callback on each tick.
|
|
38
|
+
*/
|
|
39
|
+
export interface DefinePollingTriggerPollContext<
|
|
40
|
+
TConfig extends CredentialJsonRecord,
|
|
41
|
+
TState extends JsonValue | undefined,
|
|
42
|
+
TBindings extends DefinedNodeCredentialBindings | undefined,
|
|
43
|
+
> {
|
|
44
|
+
readonly config: TConfig;
|
|
45
|
+
readonly state: TState;
|
|
46
|
+
readonly credentials: DefinedNodeCredentialAccessors<TBindings>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* What `poll` must return each tick.
|
|
51
|
+
*/
|
|
52
|
+
export interface DefinePollingTriggerPollResult<TItemJson, TState extends JsonValue | undefined> {
|
|
53
|
+
/**
|
|
54
|
+
* New items to emit. Each item may carry an optional `dedupKey`; duplicate keys are
|
|
55
|
+
* filtered out against a rolling dedup window (managed internally by the runtime).
|
|
56
|
+
* Items without a `dedupKey` are always emitted.
|
|
57
|
+
*/
|
|
58
|
+
readonly items: ReadonlyArray<{ json: TItemJson; dedupKey?: string }>;
|
|
59
|
+
/** Persisted as the trigger's setup state for the next tick. */
|
|
60
|
+
readonly nextState: TState;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Context passed into the `execute` callback for post-emit enrichment (e.g. fetching
|
|
65
|
+
* attachment bytes). Mirrors `NodeExecutionContext` so plugin authors use familiar patterns.
|
|
66
|
+
*/
|
|
67
|
+
export type DefinePollingTriggerExecuteContext<TConfig extends TriggerNodeConfig<any, any>> =
|
|
68
|
+
NodeExecutionContext<TConfig>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Context passed into the `testItems` callback.
|
|
72
|
+
*/
|
|
73
|
+
export type DefinePollingTriggerTestItemsContext<TConfig extends TriggerNodeConfig<any, any>> =
|
|
74
|
+
TriggerTestItemsContext<TConfig>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Options accepted by `definePollingTrigger`.
|
|
78
|
+
*/
|
|
79
|
+
export interface DefinePollingTriggerOptions<
|
|
80
|
+
TKey extends string,
|
|
81
|
+
TConfig extends CredentialJsonRecord,
|
|
82
|
+
TItemJson,
|
|
83
|
+
TState extends JsonValue | undefined,
|
|
84
|
+
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
85
|
+
> {
|
|
86
|
+
/**
|
|
87
|
+
* Unique node-token-id-style key, e.g. `"msgraph-mail.on-new-mail"`.
|
|
88
|
+
* Used as the persisted runtime type name — must be stable across deployments.
|
|
89
|
+
*/
|
|
90
|
+
readonly key: TKey;
|
|
91
|
+
readonly title: string;
|
|
92
|
+
readonly description?: string;
|
|
93
|
+
/** Canvas icon (same contract as `NodeConfigBase.icon`). */
|
|
94
|
+
readonly icon?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Zod schema for the trigger's user-facing configuration.
|
|
97
|
+
* When provided, the returned `create()` method is typed against the inferred config type.
|
|
98
|
+
*/
|
|
99
|
+
readonly configSchema?: ZodType<TConfig>;
|
|
100
|
+
/** Credential bindings keyed by slot (same format as `defineNode`). */
|
|
101
|
+
readonly credentials?: TBindings;
|
|
102
|
+
/**
|
|
103
|
+
* Called once when the trigger arms (or re-arms after a server restart) to provide the
|
|
104
|
+
* initial value for `state` when no persisted state exists.
|
|
105
|
+
*/
|
|
106
|
+
initialState?(): TState;
|
|
107
|
+
/**
|
|
108
|
+
* Polling interval in milliseconds. The runtime enforces a minimum of 25 ms.
|
|
109
|
+
* @default 60_000
|
|
110
|
+
*/
|
|
111
|
+
readonly pollIntervalMs?: number;
|
|
112
|
+
/**
|
|
113
|
+
* The per-tick poll logic. Called by the runtime each interval.
|
|
114
|
+
* Must return new items plus the next persisted state.
|
|
115
|
+
*/
|
|
116
|
+
poll(
|
|
117
|
+
pollCtx: DefinePollingTriggerPollContext<TConfig, TState, TBindings>,
|
|
118
|
+
): MaybePromise<DefinePollingTriggerPollResult<TItemJson, TState>>;
|
|
119
|
+
/**
|
|
120
|
+
* Optional post-emit enrichment step (runs in the normal node-execute phase after the
|
|
121
|
+
* trigger fires and the workflow run starts). Use for expensive per-item work such as
|
|
122
|
+
* fetching attachment bytes via `ctx.binary.attach`. When omitted, the trigger passes
|
|
123
|
+
* items through unchanged.
|
|
124
|
+
*/
|
|
125
|
+
execute?(
|
|
126
|
+
items: Items<TItemJson>,
|
|
127
|
+
ctx: NodeExecutionContext<DefinedPollingTriggerConfig<TConfig, TItemJson>>,
|
|
128
|
+
): MaybePromise<NodeOutputs>;
|
|
129
|
+
/**
|
|
130
|
+
* Optional implementation for the workflow UI's "Test" button. Should return a small
|
|
131
|
+
* sample of current items without consulting or mutating polling state.
|
|
132
|
+
*/
|
|
133
|
+
testItems?(
|
|
134
|
+
ctx: TriggerTestItemsContext<DefinedPollingTriggerConfig<TConfig, TItemJson>>,
|
|
135
|
+
): MaybePromise<Items<TItemJson>>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// DefinedPollingTrigger (returned object)
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The object returned by `definePollingTrigger`. Register it via
|
|
144
|
+
* `definePlugin({ nodes: [myTrigger] })` or call `.register(ctx)` directly.
|
|
145
|
+
*
|
|
146
|
+
* `poll` is also directly callable for unit-testing — no runtime needed.
|
|
147
|
+
*/
|
|
148
|
+
export interface DefinedPollingTrigger<
|
|
149
|
+
TKey extends string,
|
|
150
|
+
TConfig extends CredentialJsonRecord,
|
|
151
|
+
TItemJson,
|
|
152
|
+
TState extends JsonValue | undefined,
|
|
153
|
+
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
154
|
+
> {
|
|
155
|
+
readonly kind: "defined-polling-trigger";
|
|
156
|
+
readonly key: TKey;
|
|
157
|
+
readonly title: string;
|
|
158
|
+
readonly description?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Create the trigger config for use in workflow definitions.
|
|
161
|
+
* @param cfg - User-facing trigger configuration.
|
|
162
|
+
* @param name - Display name (defaults to `title`).
|
|
163
|
+
* @param id - Optional stable node id.
|
|
164
|
+
*/
|
|
165
|
+
create(cfg: TConfig, name?: string, id?: string): DefinedPollingTriggerConfig<TConfig, TItemJson>;
|
|
166
|
+
/**
|
|
167
|
+
* Test seam: call `poll` directly without starting the runtime.
|
|
168
|
+
* Returns `{ items, nextState }` just like the real runtime receives.
|
|
169
|
+
*/
|
|
170
|
+
poll(
|
|
171
|
+
pollCtx: Omit<DefinePollingTriggerPollContext<TConfig, TState, TBindings>, "credentials"> & {
|
|
172
|
+
credentials?: DefinedNodeCredentialAccessors<TBindings>;
|
|
173
|
+
},
|
|
174
|
+
): MaybePromise<DefinePollingTriggerPollResult<TItemJson, TState>>;
|
|
175
|
+
/** Registers the synthesised runtime class with the plugin container. */
|
|
176
|
+
register(context: { registerNode<TValue>(token: TypeToken<TValue>, implementation?: TypeToken<TValue>): void }): void;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// DefinedPollingTriggerConfig (TriggerNodeConfig shape for the engine)
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* TriggerNodeConfig produced by `DefinedPollingTrigger.create(...)`.
|
|
185
|
+
* Holds user configuration and credential requirements for the engine.
|
|
186
|
+
* The setup state type is opaque `JsonValue | undefined` — the runtime
|
|
187
|
+
* uses an internal wrapped shape that plugin authors never see.
|
|
188
|
+
*/
|
|
189
|
+
export class DefinedPollingTriggerConfig<TConfig extends CredentialJsonRecord, TItemJson> implements TriggerNodeConfig<
|
|
190
|
+
TItemJson,
|
|
191
|
+
JsonValue | undefined
|
|
192
|
+
> {
|
|
193
|
+
readonly kind = "trigger" as const;
|
|
194
|
+
readonly type: TypeToken<unknown>;
|
|
195
|
+
readonly icon: string | undefined;
|
|
196
|
+
|
|
197
|
+
constructor(
|
|
198
|
+
public readonly name: string,
|
|
199
|
+
public readonly cfg: TConfig,
|
|
200
|
+
typeToken: TypeToken<unknown>,
|
|
201
|
+
icon: string | undefined,
|
|
202
|
+
private readonly credentialRequirements: ReadonlyArray<CredentialRequirement>,
|
|
203
|
+
public readonly id?: string,
|
|
204
|
+
) {
|
|
205
|
+
this.type = typeToken;
|
|
206
|
+
this.icon = icon;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
getCredentialRequirements(): ReadonlyArray<CredentialRequirement> {
|
|
210
|
+
return this.credentialRequirements;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Internal wrapped state helpers
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
/** Opaque shape stored in the trigger setup state repository. @internal */
|
|
219
|
+
interface InternalWrappedState {
|
|
220
|
+
readonly userState: JsonValue | undefined;
|
|
221
|
+
readonly seenKeys: ReadonlyArray<string>;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function isWrappedState(value: unknown): value is InternalWrappedState {
|
|
225
|
+
return (
|
|
226
|
+
value !== null &&
|
|
227
|
+
typeof value === "object" &&
|
|
228
|
+
"seenKeys" in (value as Record<string, unknown>) &&
|
|
229
|
+
Array.isArray((value as InternalWrappedState).seenKeys)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// Implementation factory
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Declarative helper for authoring polling triggers.
|
|
239
|
+
*
|
|
240
|
+
* ```ts
|
|
241
|
+
* export const onNewMail = definePollingTrigger({
|
|
242
|
+
* key: "my-plugin.on-new-mail",
|
|
243
|
+
* title: "On new mail",
|
|
244
|
+
* configSchema: z.object({ folder: z.string() }),
|
|
245
|
+
* credentials: { auth: myOAuthCredentialType },
|
|
246
|
+
* initialState: () => ({ lastSeenId: undefined }),
|
|
247
|
+
* pollIntervalMs: 60_000,
|
|
248
|
+
* async poll({ config, state, credentials }) {
|
|
249
|
+
* const session = await credentials.auth();
|
|
250
|
+
* const messages = await fetchMessages(session, config.folder, state.lastSeenId);
|
|
251
|
+
* return {
|
|
252
|
+
* items: messages.map(m => ({ json: m, dedupKey: m.id })),
|
|
253
|
+
* nextState: { lastSeenId: messages[0]?.id ?? state.lastSeenId },
|
|
254
|
+
* };
|
|
255
|
+
* },
|
|
256
|
+
* });
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
export function definePollingTrigger<
|
|
260
|
+
TKey extends string,
|
|
261
|
+
TConfig extends CredentialJsonRecord,
|
|
262
|
+
TItemJson,
|
|
263
|
+
TState extends JsonValue | undefined,
|
|
264
|
+
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
265
|
+
>(
|
|
266
|
+
options: DefinePollingTriggerOptions<TKey, TConfig, TItemJson, TState, TBindings>,
|
|
267
|
+
): DefinedPollingTrigger<TKey, TConfig, TItemJson, TState, TBindings> {
|
|
268
|
+
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
269
|
+
const DEFAULT_INTERVAL_MS = 60_000;
|
|
270
|
+
|
|
271
|
+
type TConfig_ = DefinedPollingTriggerConfig<TConfig, TItemJson>;
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// Synthesised runtime class (implements TestableTriggerNode)
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
const DefinedPollingTriggerRuntime = class implements TestableTriggerNode<TConfig_> {
|
|
278
|
+
readonly kind = "trigger" as const;
|
|
279
|
+
readonly outputPorts = ["main"] as const;
|
|
280
|
+
|
|
281
|
+
async setup(ctx: TriggerSetupContext<TConfig_, JsonValue | undefined>): Promise<JsonValue | undefined> {
|
|
282
|
+
const cfg = ctx.config.cfg;
|
|
283
|
+
const intervalMs =
|
|
284
|
+
(cfg as Partial<{ pollIntervalMs: number }>).pollIntervalMs ?? options.pollIntervalMs ?? DEFAULT_INTERVAL_MS;
|
|
285
|
+
|
|
286
|
+
// Unwrap previously persisted state, or create the initial wrapped state.
|
|
287
|
+
const persisted = ctx.previousState;
|
|
288
|
+
const existingWrapped: InternalWrappedState | undefined = isWrappedState(persisted) ? persisted : undefined;
|
|
289
|
+
const seedWrapped: InternalWrappedState = existingWrapped ?? {
|
|
290
|
+
userState: options.initialState ? options.initialState() : undefined,
|
|
291
|
+
seenKeys: [],
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const result = await ctx.polling.start<InternalWrappedState, TItemJson>({
|
|
295
|
+
intervalMs,
|
|
296
|
+
seedState: seedWrapped,
|
|
297
|
+
runCycle: async ({ previousState }) => {
|
|
298
|
+
const wrapped: InternalWrappedState = previousState ?? seedWrapped;
|
|
299
|
+
const seenSet = new Set(wrapped.seenKeys);
|
|
300
|
+
|
|
301
|
+
const credentialAccessors = definedNodeCredentialAccessorFactory.create(
|
|
302
|
+
options.credentials,
|
|
303
|
+
ctx,
|
|
304
|
+
) as DefinedNodeCredentialAccessors<TBindings>;
|
|
305
|
+
|
|
306
|
+
const pollResult = await options.poll({
|
|
307
|
+
config: cfg,
|
|
308
|
+
state: wrapped.userState as TState,
|
|
309
|
+
credentials: credentialAccessors,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Dedup: filter items whose dedupKey is already seen
|
|
313
|
+
const newItems: Array<{ json: TItemJson }> = [];
|
|
314
|
+
const newKeys: string[] = [];
|
|
315
|
+
for (const item of pollResult.items) {
|
|
316
|
+
if (item.dedupKey !== undefined) {
|
|
317
|
+
if (seenSet.has(item.dedupKey)) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
newKeys.push(item.dedupKey);
|
|
321
|
+
}
|
|
322
|
+
newItems.push({ json: item.json });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Merge keys, cap the window at 2000 to bound state size
|
|
326
|
+
const allKeys = [...wrapped.seenKeys, ...newKeys];
|
|
327
|
+
const cappedKeys = allKeys.length > 2000 ? allKeys.slice(allKeys.length - 2000) : allKeys;
|
|
328
|
+
|
|
329
|
+
const nextWrapped: InternalWrappedState = {
|
|
330
|
+
userState: pollResult.nextState,
|
|
331
|
+
seenKeys: cappedKeys,
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
items: newItems as Items<TItemJson>,
|
|
336
|
+
nextState: nextWrapped,
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return result as JsonValue | undefined;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async execute(items: Items<TItemJson>, ctx: NodeExecutionContext<TConfig_>): Promise<NodeOutputs> {
|
|
345
|
+
if (options.execute) {
|
|
346
|
+
return await options.execute(items, ctx);
|
|
347
|
+
}
|
|
348
|
+
return { main: items };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async getTestItems(ctx: TriggerTestItemsContext<TConfig_>): Promise<Items> {
|
|
352
|
+
if (options.testItems) {
|
|
353
|
+
return await options.testItems(ctx);
|
|
354
|
+
}
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
persistedNode({ name: options.key })(DefinedPollingTriggerRuntime);
|
|
360
|
+
|
|
361
|
+
// ---------------------------------------------------------------------------
|
|
362
|
+
// Returned definition object
|
|
363
|
+
// ---------------------------------------------------------------------------
|
|
364
|
+
|
|
365
|
+
const definition: DefinedPollingTrigger<TKey, TConfig, TItemJson, TState, TBindings> = {
|
|
366
|
+
kind: "defined-polling-trigger",
|
|
367
|
+
key: options.key,
|
|
368
|
+
title: options.title,
|
|
369
|
+
description: options.description,
|
|
370
|
+
|
|
371
|
+
create(cfg: TConfig, name = options.title, id?: string): DefinedPollingTriggerConfig<TConfig, TItemJson> {
|
|
372
|
+
return new DefinedPollingTriggerConfig<TConfig, TItemJson>(
|
|
373
|
+
name,
|
|
374
|
+
cfg,
|
|
375
|
+
DefinedPollingTriggerRuntime,
|
|
376
|
+
options.icon,
|
|
377
|
+
credentialRequirements,
|
|
378
|
+
id,
|
|
379
|
+
);
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
poll(pollCtx) {
|
|
383
|
+
return options.poll({
|
|
384
|
+
config: pollCtx.config,
|
|
385
|
+
state: pollCtx.state,
|
|
386
|
+
credentials: (pollCtx.credentials ?? {}) as DefinedNodeCredentialAccessors<TBindings>,
|
|
387
|
+
});
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
register(context) {
|
|
391
|
+
context.registerNode(DefinedPollingTriggerRuntime);
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
return definition;
|
|
396
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared internal helpers for defineNode and definePollingTrigger.
|
|
3
|
+
* Not part of the public API — import only from authoring helpers.
|
|
4
|
+
*/
|
|
5
|
+
import type { AnyCredentialType, CredentialRequirement, CredentialTypeId } from "../contracts/credentialTypes";
|
|
6
|
+
import type { NodeExecutionContext } from "../contracts/runtimeTypes";
|
|
7
|
+
import type { RunnableNodeConfig } from "../contracts/workflowTypes";
|
|
8
|
+
import type { DefinedNodeCredentialAccessors, DefinedNodeCredentialBindings } from "./defineNode.types";
|
|
9
|
+
|
|
10
|
+
type ResolvableCredentialType = AnyCredentialType | CredentialTypeId;
|
|
11
|
+
|
|
12
|
+
export const definedNodeCredentialRequirementFactory = {
|
|
13
|
+
create(bindings: DefinedNodeCredentialBindings | undefined): ReadonlyArray<CredentialRequirement> {
|
|
14
|
+
if (!bindings) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return Object.entries(bindings).map(([slotKey, binding]) => {
|
|
18
|
+
if (typeof binding === "string" || this.isCredentialType(binding)) {
|
|
19
|
+
return {
|
|
20
|
+
slotKey,
|
|
21
|
+
label: this.humanize(slotKey),
|
|
22
|
+
acceptedTypes: [this.resolveTypeId(binding)],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const types = Array.isArray(binding.type) ? binding.type : [binding.type];
|
|
27
|
+
return {
|
|
28
|
+
slotKey,
|
|
29
|
+
label: binding.label ?? this.humanize(slotKey),
|
|
30
|
+
acceptedTypes: types.map((entry) => this.resolveTypeId(entry)),
|
|
31
|
+
optional: binding.optional,
|
|
32
|
+
helpText: binding.helpText,
|
|
33
|
+
helpUrl: binding.helpUrl,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
isCredentialType(value: unknown): value is AnyCredentialType {
|
|
39
|
+
return (
|
|
40
|
+
Boolean(value) &&
|
|
41
|
+
typeof value === "object" &&
|
|
42
|
+
"definition" in (value as Record<string, unknown>) &&
|
|
43
|
+
typeof (value as AnyCredentialType).definition?.typeId === "string"
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
resolveTypeId(type: ResolvableCredentialType): string {
|
|
48
|
+
return typeof type === "string" ? type : type.definition.typeId;
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
humanize(key: string): string {
|
|
52
|
+
return key
|
|
53
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
54
|
+
.replace(/[-_.]+/g, " ")
|
|
55
|
+
.replace(/\s+/g, " ")
|
|
56
|
+
.trim()
|
|
57
|
+
.replace(/^./, (character) => character.toUpperCase());
|
|
58
|
+
},
|
|
59
|
+
} as const;
|
|
60
|
+
|
|
61
|
+
export const definedNodeCredentialAccessorFactory = {
|
|
62
|
+
create<TBindings extends DefinedNodeCredentialBindings | undefined>(
|
|
63
|
+
bindings: TBindings,
|
|
64
|
+
ctx:
|
|
65
|
+
| NodeExecutionContext<RunnableNodeConfig<any, any>>
|
|
66
|
+
| { getCredential<TSession = unknown>(slotKey: string): Promise<TSession> },
|
|
67
|
+
): DefinedNodeCredentialAccessors<TBindings> {
|
|
68
|
+
if (!bindings) {
|
|
69
|
+
return {} as DefinedNodeCredentialAccessors<TBindings>;
|
|
70
|
+
}
|
|
71
|
+
const entries = Object.keys(bindings).map((slotKey) => [slotKey, () => ctx.getCredential(slotKey)] as const);
|
|
72
|
+
return Object.fromEntries(entries) as DefinedNodeCredentialAccessors<TBindings>;
|
|
73
|
+
},
|
|
74
|
+
} as const;
|
package/src/authoring/index.ts
CHANGED
|
@@ -14,3 +14,22 @@ export { defineBatchNode, defineNode } from "./defineNode.types";
|
|
|
14
14
|
export type { DefineCredentialOptions } from "./defineCredential.types";
|
|
15
15
|
export { defineCredential } from "./defineCredential.types";
|
|
16
16
|
export { callableTool } from "./callableTool.types";
|
|
17
|
+
export { DefinedCollectionRegistry } from "./DefinedCollectionRegistry";
|
|
18
|
+
export type {
|
|
19
|
+
DefinedCollection,
|
|
20
|
+
CollectionDefinition,
|
|
21
|
+
CollectionFieldDefinition,
|
|
22
|
+
CollectionIndexDefinition,
|
|
23
|
+
CollectionColumnBuilder,
|
|
24
|
+
DefineCollectionOptions,
|
|
25
|
+
} from "./defineCollection.types";
|
|
26
|
+
export { defineCollection, c } from "./defineCollection.types";
|
|
27
|
+
export type {
|
|
28
|
+
DefinePollingTriggerOptions,
|
|
29
|
+
DefinePollingTriggerPollContext,
|
|
30
|
+
DefinePollingTriggerPollResult,
|
|
31
|
+
DefinePollingTriggerExecuteContext,
|
|
32
|
+
DefinePollingTriggerTestItemsContext,
|
|
33
|
+
DefinedPollingTrigger,
|
|
34
|
+
} from "./definePollingTrigger.types";
|
|
35
|
+
export { definePollingTrigger, DefinedPollingTriggerConfig } from "./definePollingTrigger.types";
|
package/src/bootstrap/index.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
/** Composition-root engine graph and advanced runtime wiring. Not part of the main `@codemation/core` barrel. */
|
|
2
2
|
export { Engine } from "../orchestration/Engine";
|
|
3
|
+
export {
|
|
4
|
+
AbortControllerFactory,
|
|
5
|
+
TestSuiteOrchestrator,
|
|
6
|
+
TestSuiteRunIdFactory,
|
|
7
|
+
type RunTestSuiteArgs,
|
|
8
|
+
type TestSuiteCaseOutcome,
|
|
9
|
+
type TestSuiteOrchestratorEngine,
|
|
10
|
+
type TestSuiteRunResult,
|
|
11
|
+
} from "../orchestration";
|
|
3
12
|
export { EngineFactory, type EngineCompositionDeps } from "../runtime/EngineFactory";
|
|
4
13
|
export {
|
|
5
14
|
EngineRuntimeRegistrar,
|
|
@@ -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,6 +51,13 @@ export class EngineRuntimeRegistrar {
|
|
|
50
51
|
if (!container.isRegistered(RunnableOutputBehaviorResolver, true)) {
|
|
51
52
|
container.registerSingleton(RunnableOutputBehaviorResolver, RunnableOutputBehaviorResolver);
|
|
52
53
|
}
|
|
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
|
+
}
|
|
53
61
|
container.registerSingleton(EngineExecutionLimitsPolicyFactory, EngineExecutionLimitsPolicyFactory);
|
|
54
62
|
container.registerSingleton(NodeInstanceFactoryFactory, NodeInstanceFactoryFactory);
|
|
55
63
|
container.registerSingleton(DefaultAsyncSleeper, DefaultAsyncSleeper);
|
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
|
}
|