@uipath/packager-tool-workflowcompiler-browser 1.197.0 → 1.198.0-preview.80

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.
@@ -0,0 +1,2 @@
1
+ export type { CleanedWorkflowDetail, RemoteCleanedWorkflow, RemoteWorkflowCompilerCleanupPayload as RemoteProjectCleanup, WorkflowCleanupDetails, } from "@uipath/solutionpackager-tool-core";
2
+ export { toProjectCleanupInfoFromRemote as toProjectCleanupInfo } from "@uipath/solutionpackager-tool-core";
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import "./i18n/index.js";
2
+ export type { CleanedWorkflowDetail, WorkflowCleanupDetails, } from "./cleanup-result.js";
2
3
  export { WorkflowCompilerToolFactory } from "./workflow-compiler-tool-factory.js";
package/dist/index.js CHANGED
@@ -585,11 +585,15 @@ import {
585
585
 
586
586
  // src/project-bundle-executor.ts
587
587
  import {
588
+ ToolErrorCodes as ToolErrorCodes2,
588
589
  ToolResult as ToolResult2,
589
590
  toRelativeUrl as toRelativeUrl2,
590
591
  translate as translate3
591
592
  } from "@uipath/solutionpackager-tool-core";
592
593
 
594
+ // src/cleanup-result.ts
595
+ import { toProjectCleanupInfoFromRemote } from "@uipath/solutionpackager-tool-core";
596
+
593
597
  // src/pack-result-processor.ts
594
598
  import {
595
599
  ToolErrorCodes,
@@ -854,7 +858,9 @@ class PackagerHubConnection {
854
858
  errorCode: jobResult.ErrorCode,
855
859
  status: jobResult.Status,
856
860
  message: jobResult.Message,
857
- bucketId: jobResult.BucketId
861
+ bucketId: jobResult.BucketId,
862
+ changedFilesByProject: jobResult.ChangedFilesByProject,
863
+ unusedDependenciesByProject: jobResult.UnusedDependenciesByProject
858
864
  });
859
865
  this.completionResolve = null;
860
866
  this.completionReject = null;
@@ -887,6 +893,16 @@ class ProjectBundleExecutor {
887
893
  resolve: () => {}
888
894
  });
889
895
  }
896
+ async cleanupAsync(options, context) {
897
+ if (context.downloadUrl && !options.dryRun && !context.solutionId && !context.projectId) {
898
+ return ToolResult2.error(ToolErrorCodes2.InternalError, "Cleanup save-back requires a solutionId or projectId for downloaded workspaces.");
899
+ }
900
+ return this.enqueueAsync(context, {
901
+ operation: "Cleanup" /* Cleanup */,
902
+ options,
903
+ resolve: () => {}
904
+ });
905
+ }
890
906
  async enqueueAsync(context, entry) {
891
907
  const projectPath = entry.options.projectPath;
892
908
  context.logger?.info(`[BundleExecutor] ${entry.operation}Async called for project: ${projectPath}`);
@@ -1006,8 +1022,30 @@ class ProjectBundleExecutor {
1006
1022
  await this.handlePackBucketAsync(result, session, context);
1007
1023
  return;
1008
1024
  }
1025
+ if (session.operation === "Cleanup" /* Cleanup */) {
1026
+ this.resolveCleanup(result, session, context);
1027
+ return;
1028
+ }
1009
1029
  this.resolveAll(session, ToolResult2.success());
1010
1030
  }
1031
+ resolveCleanup(result, session, context) {
1032
+ for (const [projectPath, entry] of session.projects) {
1033
+ const details = {};
1034
+ if (result.changedFilesByProject) {
1035
+ details.changedFilesByProject = result.changedFilesByProject;
1036
+ }
1037
+ const designId = context.projects.find((p) => p.ProjectPath === projectPath)?.Id;
1038
+ const raw = designId ? result.unusedDependenciesByProject?.[designId] : undefined;
1039
+ if (raw) {
1040
+ details.projectCleanup = toProjectCleanupInfoFromRemote(raw);
1041
+ }
1042
+ const projectResult = ToolResult2.success();
1043
+ if (Object.keys(details).length > 0) {
1044
+ projectResult.details = details;
1045
+ }
1046
+ entry.resolve(projectResult);
1047
+ }
1048
+ }
1011
1049
  async handlePackBucketAsync(result, session, context) {
1012
1050
  if (result.bucketId == null) {
1013
1051
  this.resolveAll(session, ToolResult2.error("PackAgentError", translate3.t("toolWorkflowCompilerBrowser.errors.packSucceededNoBucket")));
@@ -1032,6 +1070,8 @@ class ProjectBundleExecutor {
1032
1070
  context.logger?.info(`[BundleExecutor] BeginSession URL: ${url}`);
1033
1071
  const firstEntry = session.projects.values().next().value;
1034
1072
  const opts = firstEntry.options;
1073
+ const skipAnalyze = "skipAnalyze" in opts ? opts.skipAnalyze : false;
1074
+ const skipValidate = "skipValidate" in opts ? opts.skipValidate : false;
1035
1075
  const rpaProjects = [...session.projects.entries()].map(([projectPath, entry]) => {
1036
1076
  const project = context.projects.find((p) => p.ProjectPath === projectPath);
1037
1077
  return {
@@ -1045,14 +1085,16 @@ class ProjectBundleExecutor {
1045
1085
  Command: session.operation,
1046
1086
  SupportedProtocolVersions: ["2.0"],
1047
1087
  DownloadUrl: context.downloadUrl ?? "",
1088
+ SolutionId: context.solutionId ?? "",
1089
+ ProjectId: context.projectId ?? "",
1048
1090
  SessionId: sessionId,
1049
1091
  ProfileKey: "StudioWeb",
1050
1092
  LicenseType: "Development",
1051
1093
  LogLevel: opts.logLevel ?? "Warning",
1052
1094
  Culture: "en-US",
1053
1095
  DownloadTimeoutInSeconds: 300,
1054
- SkipAnalyze: String(opts.skipAnalyze),
1055
- SkipValidate: String(opts.skipValidate),
1096
+ SkipAnalyze: String(skipAnalyze),
1097
+ SkipValidate: String(skipValidate),
1056
1098
  TargetFramework: context.targetFramework,
1057
1099
  RpaProjects: rpaProjects,
1058
1100
  WorkflowCompilerVersion: context.workflowCompilerVersion ?? ""
@@ -1080,6 +1122,13 @@ class ProjectBundleExecutor {
1080
1122
  executorInfo.Description = pkg.description ?? "";
1081
1123
  executorInfo.ReleaseNotes = pkg.releaseNotes ?? "";
1082
1124
  }
1125
+ if (firstEntry.operation === "Cleanup" /* Cleanup */) {
1126
+ executorInfo.DryRun = firstEntry.options.dryRun;
1127
+ executorInfo.SkipImports = firstEntry.options.skipImports;
1128
+ if (context.lockKey) {
1129
+ executorInfo.LockKey = context.lockKey;
1130
+ }
1131
+ }
1083
1132
  const inputArguments = JSON.stringify(executorInfo);
1084
1133
  const body = {
1085
1134
  source: "StudioWeb",
@@ -1166,7 +1215,7 @@ class ProjectBundleExecutor {
1166
1215
  // src/workflow-compiler-tool.ts
1167
1216
  import {
1168
1217
  ProjectTool,
1169
- ToolErrorCodes as ToolErrorCodes2,
1218
+ ToolErrorCodes as ToolErrorCodes3,
1170
1219
  ToolResult as ToolResult3
1171
1220
  } from "@uipath/solutionpackager-tool-core";
1172
1221
 
@@ -1184,7 +1233,7 @@ class WorkflowCompilerTool extends ProjectTool {
1184
1233
  }
1185
1234
  async validateAsync(options, _cancellationToken) {
1186
1235
  if (!this.context) {
1187
- return ToolResult3.error(ToolErrorCodes2.InternalError, "Validate operation requires a project operation context");
1236
+ return ToolResult3.error(ToolErrorCodes3.InternalError, "Validate operation requires a project operation context");
1188
1237
  }
1189
1238
  return this.bundleExecutor.validateAsync(options, this.context);
1190
1239
  }
@@ -1194,10 +1243,16 @@ class WorkflowCompilerTool extends ProjectTool {
1194
1243
  }
1195
1244
  async packAsync(options, _cancellationToken) {
1196
1245
  if (!this.context) {
1197
- return ToolResult3.error(ToolErrorCodes2.InternalError, "Pack operation requires a project operation context");
1246
+ return ToolResult3.error(ToolErrorCodes3.InternalError, "Pack operation requires a project operation context");
1198
1247
  }
1199
1248
  return this.bundleExecutor.packAsync(options, this.context);
1200
1249
  }
1250
+ async cleanupAsync(options, _cancellationToken) {
1251
+ if (!this.context) {
1252
+ return ToolResult3.error(ToolErrorCodes3.InternalError, "Cleanup operation requires a project operation context");
1253
+ }
1254
+ return this.bundleExecutor.cleanupAsync(options, this.context);
1255
+ }
1201
1256
  }
1202
1257
 
1203
1258
  // src/workflow-compiler-tool-factory.ts
@@ -1223,4 +1278,4 @@ export {
1223
1278
  WorkflowCompilerToolFactory
1224
1279
  };
1225
1280
 
1226
- //# debugId=7131BE6BC75CD59264756E2164756E21
1281
+ //# debugId=3B5A7D9039EF896764756E2164756E21
@@ -1,4 +1,5 @@
1
1
  import { type IToolLogger } from "@uipath/solutionpackager-tool-core";
2
+ import type { RemoteProjectCleanup } from "./cleanup-result.js";
2
3
  /** @public */
3
4
  export declare enum CompletedStatus {
4
5
  Succeeded = 0,
@@ -10,6 +11,10 @@ export interface PackAgentResult {
10
11
  status: CompletedStatus;
11
12
  message?: string;
12
13
  bucketId?: number;
14
+ /** Cleanup only: files changed per project design id, relative to each project. */
15
+ changedFilesByProject?: Record<string, string[]>;
16
+ /** Cleanup only: raw per-project cleanup payload, keyed by project design id. */
17
+ unusedDependenciesByProject?: Record<string, RemoteProjectCleanup>;
13
18
  }
14
19
  export declare class PackagerHubConnection {
15
20
  private readonly logger?;
@@ -1,11 +1,13 @@
1
- import { type IFileSystem, type IProjectPackOptions, type IProjectToolFactory, type IProjectValidateOptions, type ProjectOperationContext, ToolResult } from "@uipath/solutionpackager-tool-core";
1
+ import { type IFileSystem, type IProjectCleanupOptions, type IProjectPackOptions, type IProjectToolFactory, type IProjectValidateOptions, type ProjectOperationContext, ToolResult } from "@uipath/solutionpackager-tool-core";
2
2
  export declare enum BundleOperation {
3
3
  Pack = "Pack",
4
- Validate = "Validate"
4
+ Validate = "Validate",
5
+ Cleanup = "Cleanup"
5
6
  }
6
7
  export interface IProjectBundleExecutor {
7
8
  packAsync(options: IProjectPackOptions, context: ProjectOperationContext): Promise<ToolResult>;
8
9
  validateAsync(options: IProjectValidateOptions, context: ProjectOperationContext): Promise<ToolResult>;
10
+ cleanupAsync(options: IProjectCleanupOptions, context: ProjectOperationContext): Promise<ToolResult>;
9
11
  }
10
12
  export declare class ProjectBundleExecutor implements IProjectBundleExecutor {
11
13
  private readonly toolFactory;
@@ -22,12 +24,21 @@ export declare class ProjectBundleExecutor implements IProjectBundleExecutor {
22
24
  constructor(toolFactory: IProjectToolFactory, fileSystem: IFileSystem);
23
25
  packAsync(options: IProjectPackOptions, context: ProjectOperationContext): Promise<ToolResult>;
24
26
  validateAsync(options: IProjectValidateOptions, context: ProjectOperationContext): Promise<ToolResult>;
27
+ cleanupAsync(options: IProjectCleanupOptions, context: ProjectOperationContext): Promise<ToolResult>;
25
28
  private enqueueAsync;
26
29
  private getSessionAsync;
27
30
  private createSessionAsync;
28
31
  private runAgentAsync;
29
32
  private connectToHubAsync;
30
33
  private handleAgentResultAsync;
34
+ /**
35
+ * The agent removed unused items in-place and (for cloud solutions) saved the
36
+ * solution back to StudioWeb; there is no bucket to download. Resolve each
37
+ * project with its own result carrying the changed files (the full map, which
38
+ * solution-cleanup-service unions) and that project's cleanup payload mapped
39
+ * to the generic {@link toProjectCleanupInfo} shape.
40
+ */
41
+ private resolveCleanup;
31
42
  private handlePackBucketAsync;
32
43
  private sendBeginSessionAsync;
33
44
  /**
@@ -1,4 +1,4 @@
1
- import { type IFileSystem, type IProjectBuildOptions, type IProjectPackOptions, type IProjectRestoreOptions, type IProjectValidateOptions, type IToolLogger, type ProjectOperationContext, ProjectTool, ToolResult } from "@uipath/solutionpackager-tool-core";
1
+ import { type IFileSystem, type IProjectBuildOptions, type IProjectCleanupOptions, type IProjectPackOptions, type IProjectRestoreOptions, type IProjectValidateOptions, type IToolLogger, type ProjectOperationContext, ProjectTool, ToolResult } from "@uipath/solutionpackager-tool-core";
2
2
  import type { IProjectBundleExecutor } from "./project-bundle-executor.js";
3
3
  /**
4
4
  * Browser workflow compiler tool implementation.
@@ -14,4 +14,5 @@ export declare class WorkflowCompilerTool extends ProjectTool {
14
14
  validateAsync(options: IProjectValidateOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
15
15
  buildAsync(_options: IProjectBuildOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
16
16
  packAsync(options: IProjectPackOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
17
+ cleanupAsync(options: IProjectCleanupOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
17
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/packager-tool-workflowcompiler-browser",
3
- "version": "1.197.0",
3
+ "version": "1.198.0-preview.80",
4
4
  "description": "UiPath Workflow Compiler tool - browser implementation",
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,8 +25,8 @@
25
25
  "author": "",
26
26
  "license": "ISC",
27
27
  "peerDependencies": {
28
- "@uipath/solutionpackager-tool-core": "1.197.0",
28
+ "@uipath/solutionpackager-tool-core": "1.198.0",
29
29
  "@microsoft/signalr": ">=7 <11"
30
30
  },
31
- "gitHead": "56f68b263cec4ea1f2a39d738bd99ecf52f88fea"
31
+ "gitHead": "9b2c3c0f21a256d2f38dd28bc97e72e6f7b10a9c"
32
32
  }
@@ -0,0 +1,7 @@
1
+ export type {
2
+ CleanedWorkflowDetail,
3
+ RemoteCleanedWorkflow,
4
+ RemoteWorkflowCompilerCleanupPayload as RemoteProjectCleanup,
5
+ WorkflowCleanupDetails,
6
+ } from "@uipath/solutionpackager-tool-core";
7
+ export { toProjectCleanupInfoFromRemote as toProjectCleanupInfo } from "@uipath/solutionpackager-tool-core";
package/src/index.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import "./i18n/index.js";
2
2
 
3
+ // Tool-specific cleanup detail. Surfaced via `ProjectCleanupInfo.toolDetails`;
4
+ // only consumers that want WorkflowCompiler specifics import these types.
5
+ export type {
6
+ CleanedWorkflowDetail,
7
+ WorkflowCleanupDetails,
8
+ } from "./cleanup-result.js";
3
9
  // Workflow Compiler (browser) tool factory. Consumers register it explicitly via
4
10
  // `toolsFactoryRepository.registerProjectToolFactory(new WorkflowCompilerToolFactory())`.
5
11
  export { WorkflowCompilerToolFactory } from "./workflow-compiler-tool-factory.js";
@@ -9,6 +9,7 @@ import {
9
9
  LogMessage,
10
10
  translate,
11
11
  } from "@uipath/solutionpackager-tool-core";
12
+ import type { RemoteProjectCleanup } from "./cleanup-result.js";
12
13
 
13
14
  /** @public */
14
15
  export enum CompletedStatus {
@@ -22,6 +23,10 @@ export interface PackAgentResult {
22
23
  status: CompletedStatus;
23
24
  message?: string;
24
25
  bucketId?: number;
26
+ /** Cleanup only: files changed per project design id, relative to each project. */
27
+ changedFilesByProject?: Record<string, string[]>;
28
+ /** Cleanup only: raw per-project cleanup payload, keyed by project design id. */
29
+ unusedDependenciesByProject?: Record<string, RemoteProjectCleanup>;
25
30
  }
26
31
 
27
32
  interface ExecutorCommand<T> {
@@ -34,6 +39,8 @@ interface PackJobResult {
34
39
  Status: number;
35
40
  Message?: string;
36
41
  BucketId?: number;
42
+ ChangedFilesByProject?: Record<string, string[]>;
43
+ UnusedDependenciesByProject?: Record<string, RemoteProjectCleanup>;
37
44
  }
38
45
 
39
46
  enum PackagerCommandType {
@@ -225,6 +232,8 @@ export class PackagerHubConnection {
225
232
  status: jobResult.Status,
226
233
  message: jobResult.Message,
227
234
  bucketId: jobResult.BucketId,
235
+ changedFilesByProject: jobResult.ChangedFilesByProject,
236
+ unusedDependenciesByProject: jobResult.UnusedDependenciesByProject,
228
237
  });
229
238
  this.completionResolve = null;
230
239
  this.completionReject = null;
@@ -1,15 +1,18 @@
1
1
  import {
2
2
  type ConnectionInfo,
3
3
  type IFileSystem,
4
+ type IProjectCleanupOptions,
4
5
  type IProjectPackOptions,
5
6
  type IProjectToolFactory,
6
7
  type IProjectValidateOptions,
7
8
  type IToolLogger,
8
9
  type ProjectOperationContext,
10
+ ToolErrorCodes,
9
11
  ToolResult,
10
12
  toRelativeUrl,
11
13
  translate,
12
14
  } from "@uipath/solutionpackager-tool-core";
15
+ import { toProjectCleanupInfo } from "./cleanup-result.js";
13
16
  import { PackResultProcessor } from "./pack-result-processor.js";
14
17
  import {
15
18
  CompletedStatus,
@@ -22,6 +25,7 @@ type ResolveCallback = (result: ToolResult) => void;
22
25
  export enum BundleOperation {
23
26
  Pack = "Pack",
24
27
  Validate = "Validate",
28
+ Cleanup = "Cleanup",
25
29
  }
26
30
 
27
31
  interface PackEntry {
@@ -36,7 +40,13 @@ interface ValidateEntry {
36
40
  resolve: ResolveCallback;
37
41
  }
38
42
 
39
- type ProjectEntry = PackEntry | ValidateEntry;
43
+ interface CleanupEntry {
44
+ operation: BundleOperation.Cleanup;
45
+ options: IProjectCleanupOptions;
46
+ resolve: ResolveCallback;
47
+ }
48
+
49
+ type ProjectEntry = PackEntry | ValidateEntry | CleanupEntry;
40
50
 
41
51
  interface BundleSession {
42
52
  operation: BundleOperation;
@@ -55,6 +65,11 @@ export interface IProjectBundleExecutor {
55
65
  options: IProjectValidateOptions,
56
66
  context: ProjectOperationContext,
57
67
  ): Promise<ToolResult>;
68
+
69
+ cleanupAsync(
70
+ options: IProjectCleanupOptions,
71
+ context: ProjectOperationContext,
72
+ ): Promise<ToolResult>;
58
73
  }
59
74
 
60
75
  /**
@@ -117,6 +132,29 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
117
132
  });
118
133
  }
119
134
 
135
+ async cleanupAsync(
136
+ options: IProjectCleanupOptions,
137
+ context: ProjectOperationContext,
138
+ ): Promise<ToolResult> {
139
+ if (
140
+ context.downloadUrl &&
141
+ !options.dryRun &&
142
+ !context.solutionId &&
143
+ !context.projectId
144
+ ) {
145
+ return ToolResult.error(
146
+ ToolErrorCodes.InternalError,
147
+ "Cleanup save-back requires a solutionId or projectId for downloaded workspaces.",
148
+ );
149
+ }
150
+
151
+ return this.enqueueAsync(context, {
152
+ operation: BundleOperation.Cleanup,
153
+ options,
154
+ resolve: () => {},
155
+ });
156
+ }
157
+
120
158
  private async enqueueAsync(
121
159
  context: ProjectOperationContext,
122
160
  entry: ProjectEntry,
@@ -361,9 +399,50 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
361
399
  return;
362
400
  }
363
401
 
402
+ if (session.operation === BundleOperation.Cleanup) {
403
+ this.resolveCleanup(result, session, context);
404
+ return;
405
+ }
406
+
364
407
  this.resolveAll(session, ToolResult.success());
365
408
  }
366
409
 
410
+ /**
411
+ * The agent removed unused items in-place and (for cloud solutions) saved the
412
+ * solution back to StudioWeb; there is no bucket to download. Resolve each
413
+ * project with its own result carrying the changed files (the full map, which
414
+ * solution-cleanup-service unions) and that project's cleanup payload mapped
415
+ * to the generic {@link toProjectCleanupInfo} shape.
416
+ */
417
+ private resolveCleanup(
418
+ result: PackAgentResult,
419
+ session: BundleSession,
420
+ context: ProjectOperationContext,
421
+ ): void {
422
+ for (const [projectPath, entry] of session.projects) {
423
+ const details: Record<string, unknown> = {};
424
+ if (result.changedFilesByProject) {
425
+ details.changedFilesByProject = result.changedFilesByProject;
426
+ }
427
+
428
+ const designId = context.projects.find(
429
+ (p) => p.ProjectPath === projectPath,
430
+ )?.Id;
431
+ const raw = designId
432
+ ? result.unusedDependenciesByProject?.[designId]
433
+ : undefined;
434
+ if (raw) {
435
+ details.projectCleanup = toProjectCleanupInfo(raw);
436
+ }
437
+
438
+ const projectResult = ToolResult.success();
439
+ if (Object.keys(details).length > 0) {
440
+ projectResult.details = details;
441
+ }
442
+ entry.resolve(projectResult);
443
+ }
444
+ }
445
+
367
446
  private async handlePackBucketAsync(
368
447
  result: PackAgentResult,
369
448
  session: BundleSession,
@@ -426,6 +505,8 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
426
505
 
427
506
  const firstEntry = session.projects.values().next().value!;
428
507
  const opts = firstEntry.options;
508
+ const skipAnalyze = "skipAnalyze" in opts ? opts.skipAnalyze : false;
509
+ const skipValidate = "skipValidate" in opts ? opts.skipValidate : false;
429
510
 
430
511
  const rpaProjects = [...session.projects.entries()].map(
431
512
  ([projectPath, entry]) => {
@@ -451,14 +532,16 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
451
532
  Command: session.operation,
452
533
  SupportedProtocolVersions: ["2.0"],
453
534
  DownloadUrl: context.downloadUrl ?? "",
535
+ SolutionId: context.solutionId ?? "",
536
+ ProjectId: context.projectId ?? "",
454
537
  SessionId: sessionId,
455
538
  ProfileKey: "StudioWeb",
456
539
  LicenseType: "Development",
457
540
  LogLevel: opts.logLevel ?? "Warning",
458
541
  Culture: "en-US",
459
542
  DownloadTimeoutInSeconds: 300,
460
- SkipAnalyze: String(opts.skipAnalyze),
461
- SkipValidate: String(opts.skipValidate),
543
+ SkipAnalyze: String(skipAnalyze),
544
+ SkipValidate: String(skipValidate),
462
545
  TargetFramework: context.targetFramework,
463
546
  RpaProjects: rpaProjects,
464
547
  WorkflowCompilerVersion: context.workflowCompilerVersion ?? "",
@@ -490,6 +573,16 @@ export class ProjectBundleExecutor implements IProjectBundleExecutor {
490
573
  executorInfo.ReleaseNotes = pkg.releaseNotes ?? "";
491
574
  }
492
575
 
576
+ if (firstEntry.operation === BundleOperation.Cleanup) {
577
+ executorInfo.DryRun = firstEntry.options.dryRun;
578
+ executorInfo.SkipImports = firstEntry.options.skipImports;
579
+ // Forward the StudioWeb editing session's solution lock key so the agent's
580
+ // save-back overwrite is accepted while the solution is locked by that session.
581
+ if (context.lockKey) {
582
+ executorInfo.LockKey = context.lockKey;
583
+ }
584
+ }
585
+
493
586
  const inputArguments = JSON.stringify(executorInfo);
494
587
 
495
588
  const body: Record<string, unknown> = {
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  type IFileSystem,
3
3
  type IProjectBuildOptions,
4
+ type IProjectCleanupOptions,
4
5
  type IProjectPackOptions,
5
6
  type IProjectRestoreOptions,
6
7
  type IProjectValidateOptions,
@@ -75,4 +76,18 @@ export class WorkflowCompilerTool extends ProjectTool {
75
76
 
76
77
  return this.bundleExecutor.packAsync(options, this.context);
77
78
  }
79
+
80
+ async cleanupAsync(
81
+ options: IProjectCleanupOptions,
82
+ _cancellationToken?: AbortSignal,
83
+ ): Promise<ToolResult> {
84
+ if (!this.context) {
85
+ return ToolResult.error(
86
+ ToolErrorCodes.InternalError,
87
+ "Cleanup operation requires a project operation context",
88
+ );
89
+ }
90
+
91
+ return this.bundleExecutor.cleanupAsync(options, this.context);
92
+ }
78
93
  }