ai-ctrl-workflow-logic 0.1.2 → 0.1.3
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/package.json +10 -10
- package/src/filesystem.ts +142 -0
- package/src/index.ts +147 -4
package/package.json
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-ctrl-workflow-logic",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "WorkCtrlFlow-backed AI Ctrl Workflow adapter logic",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./src/index.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./src/index.ts",
|
|
10
|
-
"./browser": "./src/browser.ts"
|
|
10
|
+
"./browser": "./src/browser.ts",
|
|
11
|
+
"./filesystem": "./src/filesystem.ts"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
|
-
"ai-ctrl-workflow-contract": "0.1.
|
|
14
|
-
"ai-workflow-contract": "0.1.
|
|
15
|
-
"ai-workflow-logic": "0.1.
|
|
14
|
+
"ai-ctrl-workflow-contract": "0.1.1",
|
|
15
|
+
"ai-workflow-contract": "0.1.4",
|
|
16
|
+
"ai-workflow-logic": "0.1.4",
|
|
16
17
|
"instant-ctrl-flow-contract": "0.1.0",
|
|
17
|
-
"instant-ctrl-flow-logic": "
|
|
18
|
-
"work-ctrl-flow-contract": "0.1.
|
|
19
|
-
"work-ctrl-flow-logic": "0.1.
|
|
18
|
+
"instant-ctrl-flow-logic": "0.1.2",
|
|
19
|
+
"work-ctrl-flow-contract": "0.1.1",
|
|
20
|
+
"work-ctrl-flow-logic": "0.1.1"
|
|
20
21
|
},
|
|
21
22
|
"files": [
|
|
22
23
|
"src"
|
|
23
24
|
],
|
|
24
25
|
"publishConfig": {
|
|
25
|
-
"access": "public"
|
|
26
|
-
"registry": "https://registry.npmjs.com"
|
|
26
|
+
"access": "public"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import type { AICtrlWorkflowController } from 'ai-ctrl-workflow-contract';
|
|
4
|
+
import type {
|
|
5
|
+
AIAgentHostEffectPort,
|
|
6
|
+
FrozenAIAgentTaskBinding,
|
|
7
|
+
FlowClosedValue,
|
|
8
|
+
AIWorkflowProfileDurableState,
|
|
9
|
+
} from 'ai-workflow-contract';
|
|
10
|
+
import { FilesystemFlowRunCheckpointStore } from 'ai-workflow-logic/checkpoint-filesystem';
|
|
11
|
+
import { createFilesystemFlowCodeResolver, type FlowModuleImporter } from 'instant-ctrl-flow-logic/filesystem';
|
|
12
|
+
import {
|
|
13
|
+
loadMaterializedFlowInstance,
|
|
14
|
+
materializeFlowInstance,
|
|
15
|
+
type FlowInstanceSourceIdentity,
|
|
16
|
+
} from 'work-ctrl-flow-logic';
|
|
17
|
+
import type { WorkCtrlFlowSnapshot } from 'work-ctrl-flow-contract';
|
|
18
|
+
import {
|
|
19
|
+
createAICtrlWorkflowCheckpointStore,
|
|
20
|
+
createAICtrlWorkflowAgentNodeRuntimeBinder,
|
|
21
|
+
createAICtrlWorkflowController,
|
|
22
|
+
loadAICtrlWorkflowSources,
|
|
23
|
+
type CreateAICtrlWorkflowControllerOptions,
|
|
24
|
+
} from './index.js';
|
|
25
|
+
|
|
26
|
+
export interface MaterializedAICtrlWorkflowControllerOptions
|
|
27
|
+
extends Omit<CreateAICtrlWorkflowControllerOptions, 'binding' | 'store' | 'resolveCode'> {
|
|
28
|
+
readonly storeRoot: string;
|
|
29
|
+
readonly instanceId: string;
|
|
30
|
+
readonly bundleDir?: string;
|
|
31
|
+
readonly source?: FlowInstanceSourceIdentity;
|
|
32
|
+
readonly checkpointConfig: FlowClosedValue;
|
|
33
|
+
readonly durableAI: AIWorkflowProfileDurableState;
|
|
34
|
+
readonly projectCheckpointOutput?: (snapshot: WorkCtrlFlowSnapshot) => FlowClosedValue;
|
|
35
|
+
readonly importModule?: FlowModuleImporter;
|
|
36
|
+
readonly agentEffects?: AIAgentHostEffectPort;
|
|
37
|
+
readonly agentTaskProofs?: Readonly<Record<string, FrozenAIAgentTaskBinding>>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface MaterializedAICtrlWorkflowController extends AICtrlWorkflowController {
|
|
41
|
+
readonly checkpointStore: FilesystemFlowRunCheckpointStore;
|
|
42
|
+
readonly instanceId: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Frozen AICtrl definition plus canonical filesystem checkpoint owner. */
|
|
46
|
+
export function createMaterializedAICtrlWorkflowControllerFromDirectory(
|
|
47
|
+
options: MaterializedAICtrlWorkflowControllerOptions,
|
|
48
|
+
): MaterializedAICtrlWorkflowController {
|
|
49
|
+
if (options.agentEffects && (!options.agentTaskProofs || Object.keys(options.agentTaskProofs).length === 0)) {
|
|
50
|
+
throw new Error('AI_AGENT_TASK_PROOF_REQUIRED: materialized AI Ctrl Agent effects require run-freeze task proofs.');
|
|
51
|
+
}
|
|
52
|
+
const instancesDir = path.join(options.storeRoot, 'instances');
|
|
53
|
+
const instanceDir = path.join(instancesDir, options.instanceId);
|
|
54
|
+
const validateDefinition = (definitionDir: string) => {
|
|
55
|
+
loadAICtrlBinding(definitionDir);
|
|
56
|
+
};
|
|
57
|
+
const instance = fs.existsSync(instanceDir)
|
|
58
|
+
? loadMaterializedFlowInstance(instanceDir, options.instanceId, validateDefinition)
|
|
59
|
+
: options.bundleDir && options.source
|
|
60
|
+
? materializeFlowInstance({
|
|
61
|
+
definitionDir: options.bundleDir,
|
|
62
|
+
instancesDir,
|
|
63
|
+
instanceId: options.instanceId,
|
|
64
|
+
source: options.source,
|
|
65
|
+
validateDefinition,
|
|
66
|
+
})
|
|
67
|
+
: (() => {
|
|
68
|
+
if (options.bundleDir || options.source) throw new Error('bundleDir and source identity must be provided together.');
|
|
69
|
+
throw new Error('bundleDir and source identity are required for first admission.');
|
|
70
|
+
})();
|
|
71
|
+
const binding = loadAICtrlBinding(instance.definitionDir);
|
|
72
|
+
const checkpointStore = new FilesystemFlowRunCheckpointStore({ storeRoot: options.storeRoot });
|
|
73
|
+
const store = createAICtrlWorkflowCheckpointStore(
|
|
74
|
+
{ checkpointStore },
|
|
75
|
+
{
|
|
76
|
+
instanceId: options.instanceId,
|
|
77
|
+
definition: instance.descriptor.definition,
|
|
78
|
+
config: options.checkpointConfig,
|
|
79
|
+
ai: options.durableAI,
|
|
80
|
+
...(options.projectCheckpointOutput ? { projectOutput: options.projectCheckpointOutput } : {}),
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
const {
|
|
84
|
+
storeRoot: _storeRoot,
|
|
85
|
+
instanceId: _instanceId,
|
|
86
|
+
bundleDir: _bundleDir,
|
|
87
|
+
source: _source,
|
|
88
|
+
checkpointConfig: _checkpointConfig,
|
|
89
|
+
durableAI: _durableAI,
|
|
90
|
+
projectCheckpointOutput: _projectCheckpointOutput,
|
|
91
|
+
importModule: _importModule,
|
|
92
|
+
agentEffects: _agentEffects,
|
|
93
|
+
agentTaskProofs: _agentTaskProofs,
|
|
94
|
+
...controllerOptions
|
|
95
|
+
} = options;
|
|
96
|
+
const controller = createAICtrlWorkflowController({
|
|
97
|
+
...controllerOptions,
|
|
98
|
+
binding,
|
|
99
|
+
store,
|
|
100
|
+
resolveCode: createFilesystemFlowCodeResolver(options.importModule),
|
|
101
|
+
...(options.agentEffects ? {
|
|
102
|
+
bindNodeRuntime: createAICtrlWorkflowAgentNodeRuntimeBinder({
|
|
103
|
+
flowInstanceId: options.instanceId,
|
|
104
|
+
checkpointRuntime: { checkpointStore },
|
|
105
|
+
effects: options.agentEffects,
|
|
106
|
+
workflowRef: `resource://${binding.definition.fqn}`,
|
|
107
|
+
taskProofs: options.agentTaskProofs ?? {},
|
|
108
|
+
}),
|
|
109
|
+
} : {}),
|
|
110
|
+
});
|
|
111
|
+
const materializedController: AICtrlWorkflowController = {
|
|
112
|
+
...controller,
|
|
113
|
+
async start(treeId, startOptions) {
|
|
114
|
+
const existing = await controller.store.load(treeId);
|
|
115
|
+
if (existing) {
|
|
116
|
+
if (startOptions?.input !== undefined
|
|
117
|
+
&& JSON.stringify(startOptions.input) !== JSON.stringify(existing.input)) {
|
|
118
|
+
throw new Error(`AI Ctrl Workflow run ${treeId} is already bound to different start input.`);
|
|
119
|
+
}
|
|
120
|
+
const outcome = await controller.getOutcome(treeId);
|
|
121
|
+
if (!outcome) throw new Error(`AI Ctrl Workflow run ${treeId} has no durable outcome.`);
|
|
122
|
+
return outcome;
|
|
123
|
+
}
|
|
124
|
+
return controller.start(treeId, startOptions);
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
return Object.assign(materializedController, { checkpointStore, instanceId: options.instanceId });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function loadAICtrlBinding(definitionDir: string) {
|
|
131
|
+
const sources = Object.fromEntries(
|
|
132
|
+
fs.readdirSync(definitionDir, { withFileTypes: true })
|
|
133
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.xnl'))
|
|
134
|
+
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)
|
|
135
|
+
.map((entry) => [entry.name, fs.readFileSync(path.join(definitionDir, entry.name), 'utf8')]),
|
|
136
|
+
);
|
|
137
|
+
const loaded = loadAICtrlWorkflowSources(sources, { baseUri: definitionDir });
|
|
138
|
+
if (!loaded.binding || loaded.diagnostics.length > 0) {
|
|
139
|
+
throw new Error(`AICtrlWorkflow definition is invalid: ${loaded.diagnostics.map((item) => item.code).join(', ')}`);
|
|
140
|
+
}
|
|
141
|
+
return loaded.binding;
|
|
142
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,12 +5,31 @@ import type {
|
|
|
5
5
|
} from 'ai-ctrl-workflow-contract';
|
|
6
6
|
import type {
|
|
7
7
|
AICtrlWorkflowDefinitionBinding,
|
|
8
|
+
AICtrlWorkflowRunProfileState,
|
|
8
9
|
AICtrlWorkflowRuntime,
|
|
10
|
+
AIAgentHostEffectPort,
|
|
11
|
+
FrozenAIAgentTaskBinding,
|
|
12
|
+
AIWorkflowAuthoredEffects,
|
|
9
13
|
AIWorkflowEffectProvider,
|
|
14
|
+
AIWorkflowFlowRunCheckpoint,
|
|
15
|
+
AIWorkflowProfileDurableState,
|
|
10
16
|
AIWorkflowRuntimeContext,
|
|
11
|
-
|
|
17
|
+
AIWorkflowStateProjection,
|
|
18
|
+
FlowClosedObject,
|
|
19
|
+
FlowClosedValue,
|
|
20
|
+
FlowDefinitionArtifactIdentity,
|
|
21
|
+
FlowRunCheckpointKey,
|
|
22
|
+
FlowRunCheckpointRuntime,
|
|
12
23
|
} from 'ai-workflow-contract';
|
|
13
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
commitFlowRunCheckpoint,
|
|
26
|
+
createAIWorkflowRuntimeContext,
|
|
27
|
+
defineAICtrlWorkflow,
|
|
28
|
+
loadFlowRunCheckpoint,
|
|
29
|
+
projectWorkCtrlSnapshotToCheckpointData,
|
|
30
|
+
bindAIAgentProcessors,
|
|
31
|
+
} from 'ai-workflow-logic';
|
|
32
|
+
import { assertFrozenAIAgentTaskBinding } from 'ai-workflow-logic/run-freeze';
|
|
14
33
|
import type {
|
|
15
34
|
FlowCodeResolver,
|
|
16
35
|
FlowDiagnostic,
|
|
@@ -28,9 +47,99 @@ import type {
|
|
|
28
47
|
} from 'work-ctrl-flow-contract';
|
|
29
48
|
import { WorkCtrlFlowEngine, type WorkCtrlFlowDefinition, type WorkCtrlFlowEngineOptions } from 'work-ctrl-flow-logic';
|
|
30
49
|
|
|
50
|
+
export interface AICtrlWorkflowCheckpointStoreOptions {
|
|
51
|
+
readonly instanceId: string;
|
|
52
|
+
readonly definition: FlowDefinitionArtifactIdentity;
|
|
53
|
+
readonly config: FlowClosedValue;
|
|
54
|
+
readonly ai: AIWorkflowProfileDurableState;
|
|
55
|
+
readonly projectOutput?: (snapshot: WorkCtrlFlowSnapshot) => FlowClosedValue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function ctrlControllerSidecars(snapshot: WorkCtrlFlowSnapshot) {
|
|
59
|
+
return {
|
|
60
|
+
status: snapshot.status,
|
|
61
|
+
lastTickNo: snapshot.lastTickNo,
|
|
62
|
+
branchDecisions: snapshot.branchDecisions,
|
|
63
|
+
orchestration: snapshot.orchestration,
|
|
64
|
+
openWaitHandles: snapshot.openWaitHandles,
|
|
65
|
+
closedWaitHandles: snapshot.closedWaitHandles,
|
|
66
|
+
appliedResumeSignals: snapshot.appliedResumeSignals,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function ctrlNodeSidecars(snapshot: WorkCtrlFlowSnapshot) {
|
|
71
|
+
return {
|
|
72
|
+
nodeResults: snapshot.nodeResults,
|
|
73
|
+
waitResults: snapshot.waitResults,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Runtime-first adapter that makes the canonical checkpoint the WorkCtrl store owner. */
|
|
78
|
+
export function createAICtrlWorkflowCheckpointStore(
|
|
79
|
+
runtime: FlowRunCheckpointRuntime,
|
|
80
|
+
options: AICtrlWorkflowCheckpointStoreOptions,
|
|
81
|
+
): WorkCtrlFlowStore {
|
|
82
|
+
return {
|
|
83
|
+
async load(treeId) {
|
|
84
|
+
const checkpoint = await loadAICtrlWorkflowCheckpoint(runtime, {
|
|
85
|
+
instanceId: options.instanceId,
|
|
86
|
+
runId: treeId,
|
|
87
|
+
});
|
|
88
|
+
// WorkCtrlFlow mutates its working snapshot during a tick. The canonical
|
|
89
|
+
// checkpoint remains immutable; only this detached replay copy is mutable.
|
|
90
|
+
return checkpoint === undefined ? undefined : structuredClone(checkpoint.profile.snapshot);
|
|
91
|
+
},
|
|
92
|
+
async save(snapshot) {
|
|
93
|
+
const durableSnapshot = projectWorkCtrlSnapshotToCheckpointData(snapshot);
|
|
94
|
+
const key = { instanceId: options.instanceId, runId: snapshot.treeId };
|
|
95
|
+
const current = await loadAICtrlWorkflowCheckpoint(runtime, key);
|
|
96
|
+
const expectedVersion = current?.version ?? null;
|
|
97
|
+
const checkpoint: AIWorkflowFlowRunCheckpoint = {
|
|
98
|
+
schemaVersion: 'depa.flow-run-checkpoint/v1',
|
|
99
|
+
instanceId: options.instanceId,
|
|
100
|
+
runId: snapshot.treeId,
|
|
101
|
+
definition: options.definition,
|
|
102
|
+
version: current === undefined ? 0 : current.version + 1,
|
|
103
|
+
input: current?.input ?? snapshot.input as FlowClosedValue,
|
|
104
|
+
config: current?.config ?? options.config,
|
|
105
|
+
state: durableSnapshot.vars as FlowClosedValue,
|
|
106
|
+
output: options.projectOutput?.(snapshot) ?? current?.output ?? null,
|
|
107
|
+
controllerSidecars: ctrlControllerSidecars(durableSnapshot) as unknown as FlowClosedObject,
|
|
108
|
+
nodeSidecars: ctrlNodeSidecars(durableSnapshot) as unknown as FlowClosedObject,
|
|
109
|
+
profile: {
|
|
110
|
+
kind: 'AICtrlWorkflow',
|
|
111
|
+
snapshot: durableSnapshot as unknown as FlowClosedObject,
|
|
112
|
+
ai: current?.profile.kind === 'AICtrlWorkflow' ? current.profile.ai : options.ai,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
await commitFlowRunCheckpoint(runtime, { expectedVersion, checkpoint });
|
|
116
|
+
},
|
|
117
|
+
async remove(treeId) {
|
|
118
|
+
throw new Error(`Canonical FlowRunCheckpoint removal is not supported for ${treeId}.`);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function loadAICtrlWorkflowCheckpoint(
|
|
124
|
+
runtime: FlowRunCheckpointRuntime,
|
|
125
|
+
input: FlowRunCheckpointKey,
|
|
126
|
+
): Promise<
|
|
127
|
+
| (AIWorkflowFlowRunCheckpoint & { readonly profile: AICtrlWorkflowRunProfileState<WorkCtrlFlowSnapshot> })
|
|
128
|
+
| undefined
|
|
129
|
+
> {
|
|
130
|
+
const checkpoint = await loadFlowRunCheckpoint(runtime, input);
|
|
131
|
+
if (checkpoint === undefined) return undefined;
|
|
132
|
+
if (checkpoint.profile.kind !== 'AICtrlWorkflow') {
|
|
133
|
+
throw new Error(`Flow run ${input.runId} is not an AI Ctrl Workflow checkpoint.`);
|
|
134
|
+
}
|
|
135
|
+
return checkpoint as AIWorkflowFlowRunCheckpoint & {
|
|
136
|
+
readonly profile: AICtrlWorkflowRunProfileState<WorkCtrlFlowSnapshot>;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
31
140
|
export interface CreateAICtrlWorkflowRuntimeOptions<
|
|
32
141
|
Effects extends AIWorkflowEffectProvider,
|
|
33
|
-
Store extends
|
|
142
|
+
Store extends AIWorkflowStateProjection,
|
|
34
143
|
> {
|
|
35
144
|
readonly runtime?: WorkCtrlFlowRuntime;
|
|
36
145
|
readonly ai: AIWorkflowRuntimeContext<Effects, Store>;
|
|
@@ -38,7 +147,7 @@ export interface CreateAICtrlWorkflowRuntimeOptions<
|
|
|
38
147
|
|
|
39
148
|
export function createAICtrlWorkflowRuntime<
|
|
40
149
|
Effects extends AIWorkflowEffectProvider,
|
|
41
|
-
Store extends
|
|
150
|
+
Store extends AIWorkflowStateProjection,
|
|
42
151
|
>(
|
|
43
152
|
options: CreateAICtrlWorkflowRuntimeOptions<Effects, Store>,
|
|
44
153
|
): AICtrlWorkflowRuntime<Effects, Store> {
|
|
@@ -48,6 +157,40 @@ export function createAICtrlWorkflowRuntime<
|
|
|
48
157
|
};
|
|
49
158
|
}
|
|
50
159
|
|
|
160
|
+
export interface CreateAICtrlWorkflowAgentNodeRuntimeBinderOptions {
|
|
161
|
+
readonly flowInstanceId: string;
|
|
162
|
+
readonly checkpointRuntime: FlowRunCheckpointRuntime;
|
|
163
|
+
readonly effects: AIAgentHostEffectPort;
|
|
164
|
+
readonly workflowRef: `resource://${string}`;
|
|
165
|
+
readonly taskProofs: Readonly<Record<string, FrozenAIAgentTaskBinding>>;
|
|
166
|
+
readonly metadata?: FlowClosedObject;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Composition seam: ordinary WorkCtrl Run nodes receive the same-named bound facade. */
|
|
170
|
+
export function createAICtrlWorkflowAgentNodeRuntimeBinder(
|
|
171
|
+
options: CreateAICtrlWorkflowAgentNodeRuntimeBinderOptions,
|
|
172
|
+
): NonNullable<WorkCtrlFlowEngineOptions['bindNodeRuntime']> {
|
|
173
|
+
return (runtime, identity) => {
|
|
174
|
+
const workflowRuntime = runtime as AICtrlWorkflowRuntime<any, AIWorkflowStateProjection>;
|
|
175
|
+
if (!workflowRuntime.ai) throw new Error('AI Ctrl node runtime requires an AI runtime context.');
|
|
176
|
+
const proof = options.taskProofs[identity.nodeId];
|
|
177
|
+
if (!proof) throw new Error(`AI_AGENT_TASK_PROOF_REQUIRED: no frozen proof for node ${identity.nodeId}.`);
|
|
178
|
+
const taskBinding = assertFrozenAIAgentTaskBinding(proof).task;
|
|
179
|
+
const effects: AIWorkflowAuthoredEffects = bindAIAgentProcessors({
|
|
180
|
+
checkpointRuntime: options.checkpointRuntime,
|
|
181
|
+
checkpointKey: { instanceId: options.flowInstanceId, runId: identity.treeId },
|
|
182
|
+
nodeId: identity.nodeId,
|
|
183
|
+
invocationKey: identity.invocationKey,
|
|
184
|
+
workflowKind: 'AICtrlWorkflow',
|
|
185
|
+
workflowRef: options.workflowRef,
|
|
186
|
+
taskBinding,
|
|
187
|
+
effects: options.effects,
|
|
188
|
+
...(options.metadata === undefined ? {} : { metadata: options.metadata }),
|
|
189
|
+
});
|
|
190
|
+
return Object.freeze({ ...runtime, ai: Object.freeze({ ...workflowRuntime.ai, effects }) });
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
51
194
|
export interface CreateAICtrlWorkflowControllerOptions
|
|
52
195
|
extends Omit<WorkCtrlFlowEngineOptions, 'definition' | 'runtime' | 'expectedForm'> {
|
|
53
196
|
readonly binding: AICtrlWorkflowDefinitionBinding;
|