@synth-deploy/server 1.0.6 → 1.2.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.
Files changed (97) hide show
  1. package/dist/agent/envoy-client.d.ts +65 -15
  2. package/dist/agent/envoy-client.d.ts.map +1 -1
  3. package/dist/agent/envoy-client.js +58 -8
  4. package/dist/agent/envoy-client.js.map +1 -1
  5. package/dist/agent/stale-deployment-detector.js +1 -1
  6. package/dist/agent/stale-deployment-detector.js.map +1 -1
  7. package/dist/agent/synth-agent.d.ts +7 -5
  8. package/dist/agent/synth-agent.d.ts.map +1 -1
  9. package/dist/agent/synth-agent.js +59 -50
  10. package/dist/agent/synth-agent.js.map +1 -1
  11. package/dist/alert-webhooks/alert-parsers.d.ts +21 -0
  12. package/dist/alert-webhooks/alert-parsers.d.ts.map +1 -0
  13. package/dist/alert-webhooks/alert-parsers.js +184 -0
  14. package/dist/alert-webhooks/alert-parsers.js.map +1 -0
  15. package/dist/api/agent.d.ts +0 -6
  16. package/dist/api/agent.d.ts.map +1 -1
  17. package/dist/api/agent.js +6 -459
  18. package/dist/api/agent.js.map +1 -1
  19. package/dist/api/alert-webhooks.d.ts +13 -0
  20. package/dist/api/alert-webhooks.d.ts.map +1 -0
  21. package/dist/api/alert-webhooks.js +279 -0
  22. package/dist/api/alert-webhooks.js.map +1 -0
  23. package/dist/api/envoy-reports.js +2 -2
  24. package/dist/api/envoy-reports.js.map +1 -1
  25. package/dist/api/envoys.js +1 -1
  26. package/dist/api/envoys.js.map +1 -1
  27. package/dist/api/fleet.d.ts.map +1 -1
  28. package/dist/api/fleet.js +14 -15
  29. package/dist/api/fleet.js.map +1 -1
  30. package/dist/api/graph.js +3 -3
  31. package/dist/api/graph.js.map +1 -1
  32. package/dist/api/operations.d.ts +7 -0
  33. package/dist/api/operations.d.ts.map +1 -0
  34. package/dist/api/operations.js +1900 -0
  35. package/dist/api/operations.js.map +1 -0
  36. package/dist/api/partitions.js +1 -1
  37. package/dist/api/partitions.js.map +1 -1
  38. package/dist/api/schemas.d.ts +434 -133
  39. package/dist/api/schemas.d.ts.map +1 -1
  40. package/dist/api/schemas.js +53 -25
  41. package/dist/api/schemas.js.map +1 -1
  42. package/dist/api/system.d.ts.map +1 -1
  43. package/dist/api/system.js +22 -21
  44. package/dist/api/system.js.map +1 -1
  45. package/dist/artifact-analyzer.js +2 -2
  46. package/dist/artifact-analyzer.js.map +1 -1
  47. package/dist/fleet/fleet-executor.js +3 -3
  48. package/dist/fleet/fleet-executor.js.map +1 -1
  49. package/dist/graph/graph-executor.d.ts.map +1 -1
  50. package/dist/graph/graph-executor.js +18 -4
  51. package/dist/graph/graph-executor.js.map +1 -1
  52. package/dist/index.js +89 -61
  53. package/dist/index.js.map +1 -1
  54. package/dist/mcp/resources.js +3 -3
  55. package/dist/mcp/resources.js.map +1 -1
  56. package/dist/mcp/tools.d.ts.map +1 -1
  57. package/dist/mcp/tools.js +2 -9
  58. package/dist/mcp/tools.js.map +1 -1
  59. package/dist/middleware/auth.js +1 -1
  60. package/dist/middleware/auth.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/agent/envoy-client.ts +111 -19
  63. package/src/agent/stale-deployment-detector.ts +1 -1
  64. package/src/agent/synth-agent.ts +76 -56
  65. package/src/alert-webhooks/alert-parsers.ts +291 -0
  66. package/src/api/agent.ts +9 -528
  67. package/src/api/alert-webhooks.ts +354 -0
  68. package/src/api/envoy-reports.ts +2 -2
  69. package/src/api/envoys.ts +1 -1
  70. package/src/api/fleet.ts +14 -15
  71. package/src/api/graph.ts +3 -3
  72. package/src/api/operations.ts +2260 -0
  73. package/src/api/partitions.ts +1 -1
  74. package/src/api/schemas.ts +59 -27
  75. package/src/api/system.ts +23 -21
  76. package/src/artifact-analyzer.ts +2 -2
  77. package/src/fleet/fleet-executor.ts +3 -3
  78. package/src/graph/graph-executor.ts +18 -4
  79. package/src/index.ts +91 -61
  80. package/src/mcp/resources.ts +3 -3
  81. package/src/mcp/tools.ts +5 -9
  82. package/src/middleware/auth.ts +1 -1
  83. package/tests/agent-mode.test.ts +5 -376
  84. package/tests/api-handlers.test.ts +27 -27
  85. package/tests/composite-operations.test.ts +557 -0
  86. package/tests/decision-diary.test.ts +62 -63
  87. package/tests/diary-reader.test.ts +14 -18
  88. package/tests/mcp-tools.test.ts +1 -1
  89. package/tests/orchestration.test.ts +34 -30
  90. package/tests/partition-isolation.test.ts +4 -9
  91. package/tests/rbac-enforcement.test.ts +8 -8
  92. package/tests/ui-journey.test.ts +9 -9
  93. package/dist/api/deployments.d.ts +0 -11
  94. package/dist/api/deployments.d.ts.map +0 -1
  95. package/dist/api/deployments.js +0 -1098
  96. package/dist/api/deployments.js.map +0 -1
  97. package/src/api/deployments.ts +0 -1347
@@ -85,7 +85,7 @@ export function registerPartitionRoutes(
85
85
  // Log cascade deletion to Decision Diary
86
86
  debrief.record({
87
87
  partitionId: id,
88
- deploymentId: null,
88
+ operationId: null,
89
89
  agent: "server",
90
90
  decisionType: "system",
91
91
  decision: `Cascade-deleted partition "${partition.name}" with ${linkedDeployments.length} deployment(s)`,
@@ -211,6 +211,17 @@ export const UpdateSettingsSchema = z.object({
211
211
  description: z.string().optional(),
212
212
  })).optional(),
213
213
  llm: LlmProviderConfigSchema.partial().optional(),
214
+ approvalDefaults: z.object({
215
+ query: z.enum(["auto", "required"]).optional(),
216
+ investigate: z.enum(["auto", "required"]).optional(),
217
+ trigger: z.enum(["auto", "required"]).optional(),
218
+ deploy: z.enum(["auto", "required"]).optional(),
219
+ maintain: z.enum(["auto", "required"]).optional(),
220
+ composite: z.enum(["auto", "required"]).optional(),
221
+ environmentOverrides: z.record(
222
+ z.record(z.enum(["auto", "required"])).optional(),
223
+ ).optional(),
224
+ }).optional(),
214
225
  });
215
226
 
216
227
  // --- Artifacts (update) ---
@@ -232,6 +243,37 @@ export const CreateDeploymentSchema = z.object({
232
243
  version: z.string().optional(),
233
244
  });
234
245
 
246
+ const ChildOperationInputSchema = z.discriminatedUnion("type", [
247
+ z.object({ type: z.literal("deploy"), artifactId: z.string().min(1), artifactVersionId: z.string().optional() }),
248
+ z.object({ type: z.literal("maintain"), intent: z.string().min(1) }),
249
+ z.object({ type: z.literal("query"), intent: z.string().min(1) }),
250
+ z.object({ type: z.literal("investigate"), intent: z.string().min(1), allowWrite: z.boolean().optional() }),
251
+ z.object({ type: z.literal("trigger"), condition: z.string().min(1), responseIntent: z.string().min(1) }),
252
+ ]);
253
+
254
+ // --- Operations ---
255
+
256
+ export const CreateOperationSchema = z.object({
257
+ artifactId: z.string().min(1).optional(),
258
+ environmentId: z.string().min(1).optional(),
259
+ partitionId: z.string().optional(),
260
+ envoyId: z.string().optional(),
261
+ version: z.string().optional(),
262
+ type: z.enum(["deploy", "maintain", "query", "investigate", "trigger", "composite"]).default("deploy"),
263
+ intent: z.string().optional(),
264
+ allowWrite: z.boolean().optional(),
265
+ /** Trigger-specific: condition expression (e.g. "disk_usage > 85") */
266
+ condition: z.string().optional(),
267
+ /** Trigger-specific: what to do when the condition fires */
268
+ responseIntent: z.string().optional(),
269
+ /** Parent operation that spawned this one (e.g. investigation → resolution) */
270
+ parentOperationId: z.string().optional(),
271
+ /** Override to require manual approval even when auto-approve would apply */
272
+ requireApproval: z.boolean().optional(),
273
+ /** Composite-specific: child operations to execute sequentially */
274
+ operations: z.array(ChildOperationInputSchema).optional(),
275
+ });
276
+
235
277
  export const ApproveDeploymentSchema = z.object({
236
278
  approvedBy: z.string().min(1),
237
279
  modifications: z.string().optional(),
@@ -242,37 +284,33 @@ export const RejectDeploymentSchema = z.object({
242
284
  });
243
285
 
244
286
  export const ModifyDeploymentPlanSchema = z.object({
245
- steps: z.array(z.object({
287
+ executionScript: z.string().min(1, "Execution script must not be empty"),
288
+ rollbackScript: z.string().optional(),
289
+ reason: z.string().min(1),
290
+ });
291
+
292
+ const ScriptedPlanSchema = z.object({
293
+ platform: z.enum(["bash", "powershell"]),
294
+ executionScript: z.string().min(1),
295
+ dryRunScript: z.string().nullable(),
296
+ rollbackScript: z.string().nullable(),
297
+ reasoning: z.string().min(1),
298
+ stepSummary: z.array(z.object({
246
299
  description: z.string().min(1),
247
- action: z.string().min(1),
248
- target: z.string().min(1),
249
300
  reversible: z.boolean(),
250
- rollbackAction: z.string().optional(),
251
- })).min(1, "Plan must contain at least one step"),
252
- reason: z.string().min(1),
301
+ })),
302
+ diffFromCurrent: z.array(z.object({ key: z.string(), from: z.string(), to: z.string() })).optional(),
253
303
  });
254
304
 
255
305
  export const SubmitPlanSchema = z.object({
256
306
  plan: z.object({
257
- steps: z.array(z.object({
258
- description: z.string().min(1),
259
- action: z.string().min(1),
260
- target: z.string().min(1),
261
- reversible: z.boolean(),
262
- rollbackAction: z.string().optional(),
263
- })).min(1),
307
+ scriptedPlan: ScriptedPlanSchema,
264
308
  reasoning: z.string().min(1),
265
309
  diffFromCurrent: z.array(z.object({ key: z.string(), from: z.string(), to: z.string() })).optional(),
266
310
  diffFromPreviousPlan: z.string().optional(),
267
311
  }),
268
312
  rollbackPlan: z.object({
269
- steps: z.array(z.object({
270
- description: z.string().min(1),
271
- action: z.string().min(1),
272
- target: z.string().min(1),
273
- reversible: z.boolean(),
274
- rollbackAction: z.string().optional(),
275
- })),
313
+ scriptedPlan: ScriptedPlanSchema,
276
314
  reasoning: z.string().min(1),
277
315
  }),
278
316
  });
@@ -291,6 +329,7 @@ export const DebriefQuerySchema = z.object({
291
329
  limit: z.coerce.number().int().positive().optional(),
292
330
  partitionId: z.string().optional(),
293
331
  decisionType: z.string().optional(),
332
+ q: z.string().optional(),
294
333
  });
295
334
 
296
335
  // --- Progress Events (from envoy callback) ---
@@ -325,13 +364,6 @@ export const TelemetryQuerySchema = z.object({
325
364
  offset: z.coerce.number().int().nonnegative().optional(),
326
365
  });
327
366
 
328
- // --- Agent ---
329
-
330
- export const QueryRequestSchema = z.object({
331
- query: z.string().min(1),
332
- conversationId: z.string().optional(),
333
- });
334
-
335
367
  // --- Auth ---
336
368
 
337
369
  export const LoginSchema = z.object({
package/src/api/system.ts CHANGED
@@ -156,6 +156,8 @@ export function registerSystemRoutes(
156
156
  const allEnvoys = envoyRegistry.list();
157
157
  const allDeployments = deployments.list();
158
158
  const allEnvironments = environments.list();
159
+ const getArtifactId = (op: (typeof allDeployments)[number]): string =>
160
+ op.input?.type === "deploy" ? (op.input as { type: "deploy"; artifactId: string }).artifactId : "";
159
161
  const allPartitions = partitions.list();
160
162
 
161
163
  // --- Stats ---
@@ -264,11 +266,11 @@ export function registerSystemRoutes(
264
266
  { time: nowTime(), event: "Signal raised" },
265
267
  ],
266
268
  relatedDeployments: recentToEnvoy.map((d) => {
267
- const artName = allArtifacts.find((a) => a.id === d.artifactId)?.name ?? d.artifactId.slice(0, 8);
269
+ const artName = allArtifacts.find((a) => a.id === getArtifactId(d))?.name ?? getArtifactId(d).slice(0, 8);
268
270
  const envName = allEnvironments.find((e) => e.id === d.environmentId)?.name ?? "unknown";
269
271
  return {
270
272
  artifact: artName,
271
- version: d.version,
273
+ version: d.version ?? "",
272
274
  target: envName,
273
275
  status: d.status,
274
276
  time: timeAgo(d.createdAt),
@@ -291,9 +293,9 @@ export function registerSystemRoutes(
291
293
  type FailureGroup = { artifactId: string; environmentId: string | undefined; failures: typeof recentFailures };
292
294
  const failureGroups = new Map<string, FailureGroup>();
293
295
  for (const dep of recentFailures) {
294
- const key = `${dep.artifactId}::${dep.environmentId}`;
296
+ const key = `${getArtifactId(dep)}::${dep.environmentId}`;
295
297
  if (!failureGroups.has(key)) {
296
- failureGroups.set(key, { artifactId: dep.artifactId, environmentId: dep.environmentId, failures: [] });
298
+ failureGroups.set(key, { artifactId: getArtifactId(dep), environmentId: dep.environmentId, failures: [] });
297
299
  }
298
300
  failureGroups.get(key)!.failures.push(dep);
299
301
  }
@@ -303,7 +305,7 @@ export function registerSystemRoutes(
303
305
 
304
306
  const hasRecovery = allDeployments.some(
305
307
  (d) =>
306
- d.artifactId === group.artifactId &&
308
+ getArtifactId(d) === group.artifactId &&
307
309
  d.environmentId === group.environmentId &&
308
310
  d.status === "succeeded" &&
309
311
  new Date(d.createdAt).getTime() > new Date(group.failures[0].createdAt).getTime(),
@@ -320,7 +322,7 @@ export function registerSystemRoutes(
320
322
  const prevSuccessful = allDeployments
321
323
  .filter(
322
324
  (d) =>
323
- d.artifactId === group.artifactId &&
325
+ getArtifactId(d) === group.artifactId &&
324
326
  d.environmentId === group.environmentId &&
325
327
  d.status === "succeeded",
326
328
  )
@@ -379,7 +381,7 @@ export function registerSystemRoutes(
379
381
  ],
380
382
  relatedDeployments: sorted.map((d) => ({
381
383
  artifact: artifactName,
382
- version: d.version,
384
+ version: d.version ?? "",
383
385
  target: envName,
384
386
  status: d.status,
385
387
  time: timeAgo(d.createdAt),
@@ -397,7 +399,7 @@ export function registerSystemRoutes(
397
399
  type EnvLatest = { dep: (typeof succeededDeps)[0]; envName: string };
398
400
  const latestByTarget = new Map<string, EnvLatest>();
399
401
  for (const dep of succeededDeps) {
400
- const key = `${dep.artifactId}::${dep.environmentId}`;
402
+ const key = `${getArtifactId(dep)}::${dep.environmentId}`;
401
403
  const existing = latestByTarget.get(key);
402
404
  if (!existing || new Date(dep.createdAt) > new Date(existing.dep.createdAt)) {
403
405
  const envName = allEnvironments.find((e) => e.id === dep.environmentId)?.name ?? "unknown";
@@ -408,13 +410,13 @@ export function registerSystemRoutes(
408
410
  for (const { dep, envName } of latestByTarget.values()) {
409
411
  if (new Date(dep.createdAt).getTime() > thirtyDaysAgo) continue; // Not stale yet
410
412
 
411
- const artifactName = allArtifacts.find((a) => a.id === dep.artifactId)?.name ?? "unknown";
413
+ const artifactName = allArtifacts.find((a) => a.id === getArtifactId(dep))?.name ?? "unknown";
412
414
  const weeksAgo = Math.floor((now - new Date(dep.createdAt).getTime()) / (7 * 24 * 60 * 60 * 1000));
413
415
 
414
416
  // Check if newer versions of this artifact have been deployed to any other environment
415
417
  const newerElsewhere = succeededDeps.filter(
416
418
  (d) =>
417
- d.artifactId === dep.artifactId &&
419
+ getArtifactId(d) === getArtifactId(dep) &&
418
420
  d.environmentId !== dep.environmentId &&
419
421
  new Date(d.createdAt).getTime() > new Date(dep.createdAt).getTime(),
420
422
  );
@@ -427,7 +429,7 @@ export function registerSystemRoutes(
427
429
  severity: "info",
428
430
  title: `Stale deployment: ${artifactName} in ${envName}`,
429
431
  detail: `v${dep.version} deployed ${weeksAgo}w ago. ${newerVersions.length} newer version${newerVersions.length > 1 ? "s" : ""} running elsewhere. May be intentional.`,
430
- relatedEntity: { type: "artifact", id: dep.artifactId, name: artifactName },
432
+ relatedEntity: { type: "artifact", id: getArtifactId(dep), name: artifactName },
431
433
  investigation: {
432
434
  title: `Stale Deployment — ${artifactName} in ${envName}`,
433
435
  entity: `${artifactName} in ${envName}`,
@@ -461,10 +463,10 @@ export function registerSystemRoutes(
461
463
  { time: nowTime(), event: `Signal raised — ${weeksAgo}w without update, newer versions exist` },
462
464
  ],
463
465
  relatedDeployments: [
464
- { artifact: artifactName, version: dep.version, target: envName, status: "succeeded", time: timeAgo(dep.createdAt) },
466
+ { artifact: artifactName, version: dep.version ?? "", target: envName, status: "succeeded", time: timeAgo(dep.createdAt) },
465
467
  ...newerElsewhere.slice(0, 3).map((d) => {
466
468
  const env = allEnvironments.find((e) => e.id === d.environmentId)?.name ?? "unknown";
467
- return { artifact: artifactName, version: d.version, target: env, status: d.status, time: timeAgo(d.createdAt) };
469
+ return { artifact: artifactName, version: d.version ?? "", target: env, status: d.status, time: timeAgo(d.createdAt) };
468
470
  }),
469
471
  ],
470
472
  },
@@ -475,11 +477,11 @@ export function registerSystemRoutes(
475
477
  // pattern that suggests a missed or skipped promotion.
476
478
  const artifactEnvVersions = new Map<string, Map<string, { version: string; deployedAt: Date }>>();
477
479
  for (const { dep, envName } of latestByTarget.values()) {
478
- if (!artifactEnvVersions.has(dep.artifactId)) {
479
- artifactEnvVersions.set(dep.artifactId, new Map());
480
+ if (!artifactEnvVersions.has(getArtifactId(dep))) {
481
+ artifactEnvVersions.set(getArtifactId(dep), new Map());
480
482
  }
481
- artifactEnvVersions.get(dep.artifactId)!.set(envName, {
482
- version: dep.version,
483
+ artifactEnvVersions.get(getArtifactId(dep))!.set(envName, {
484
+ version: dep.version ?? "",
483
485
  deployedAt: new Date(dep.createdAt),
484
486
  });
485
487
  }
@@ -503,7 +505,7 @@ export function registerSystemRoutes(
503
505
 
504
506
  // Also require that the ahead env has more recent deployments of this artifact (not just same artifact)
505
507
  const aheadHasMultiple = succeededDeps.filter(
506
- (d) => d.artifactId === artifactId &&
508
+ (d) => getArtifactId(d) === artifactId &&
507
509
  allEnvironments.find((e) => e.id === d.environmentId)?.name === aheadEnv,
508
510
  ).length >= 2;
509
511
  if (!aheadHasMultiple) continue;
@@ -620,10 +622,10 @@ export function registerSystemRoutes(
620
622
  { time: nowTime(), event: "Signal raised" },
621
623
  ],
622
624
  relatedDeployments: recentToEnv.map((d) => {
623
- const artName = allArtifacts.find((a) => a.id === d.artifactId)?.name ?? d.artifactId.slice(0, 8);
625
+ const artName = allArtifacts.find((a) => a.id === getArtifactId(d))?.name ?? getArtifactId(d).slice(0, 8);
624
626
  return {
625
627
  artifact: artName,
626
- version: d.version,
628
+ version: d.version ?? "",
627
629
  target: env.name,
628
630
  status: d.status,
629
631
  time: timeAgo(d.createdAt),
@@ -682,7 +684,7 @@ export function registerSystemRoutes(
682
684
  detail = infos[0].detail;
683
685
  } else if (activeDeployments.length > 0) {
684
686
  const d = activeDeployments[0];
685
- const artName = allArtifacts.find((a) => a.id === d.artifactId)?.name ?? "A deployment";
687
+ const artName = allArtifacts.find((a) => a.id === getArtifactId(d))?.name ?? "A deployment";
686
688
  const envName = allEnvironments.find((e) => e.id === d.environmentId)?.name ?? "target";
687
689
  headline = "Deployment in progress.";
688
690
  detail = `${artName} is being deployed to ${envName}. All other environments are stable.`;
@@ -367,7 +367,7 @@ Produce a JSON analysis that incorporates all user corrections. Raise confidence
367
367
 
368
368
  this._debrief.record({
369
369
  partitionId: null,
370
- deploymentId: null,
370
+ operationId: null,
371
371
  agent: "server",
372
372
  decisionType: "artifact-analysis",
373
373
  decision: `Re-analyzed "${artifact.name}" with ${artifact.annotations.length} user correction(s). Confidence: ${revised.confidence}.`,
@@ -406,7 +406,7 @@ Produce a JSON analysis that incorporates all user corrections. Raise confidence
406
406
 
407
407
  this._debrief.record({
408
408
  partitionId: null,
409
- deploymentId: null,
409
+ operationId: null,
410
410
  agent: "server",
411
411
  decisionType: "artifact-analysis",
412
412
  decision: `Analyzed artifact "${artifact.name}" (${artifactType}) via ${method}. ` +
@@ -98,12 +98,12 @@ export class FleetExecutor {
98
98
 
99
99
  const client = this.createEnvoyClient(entry.url, entry.token);
100
100
  try {
101
- const validation = await client.validatePlan(plan.steps);
101
+ const validation = await client.validatePlan(plan.scriptedPlan);
102
102
  results.push({
103
103
  envoyId,
104
104
  envoyName: entry.name,
105
105
  validated: validation.valid,
106
- issues: validation.violations?.map((v) => v.reason),
106
+ issues: validation.violations,
107
107
  });
108
108
  } catch (err) {
109
109
  results.push({
@@ -197,7 +197,7 @@ export class FleetExecutor {
197
197
  const client = this.createEnvoyClient(entry.url, entry.token);
198
198
  try {
199
199
  await client.executeApprovedPlan({
200
- deploymentId: fleetDeployment.id,
200
+ operationId: fleetDeployment.id,
201
201
  plan,
202
202
  rollbackPlan: rollbackPlan ?? plan,
203
203
  artifactType: "fleet",
@@ -243,10 +243,17 @@ export class GraphExecutor {
243
243
  : plan;
244
244
 
245
245
  const result: EnvoyDeployResult = await client.executeApprovedPlan({
246
- deploymentId: node.deploymentId ?? node.id,
246
+ operationId: node.deploymentId ?? node.id,
247
247
  plan: enrichedPlan,
248
248
  rollbackPlan: {
249
- steps: [],
249
+ scriptedPlan: {
250
+ platform: "bash",
251
+ executionScript: "# No rollback plan provided",
252
+ dryRunScript: null,
253
+ rollbackScript: null,
254
+ reasoning: "No rollback plan provided",
255
+ stepSummary: [],
256
+ },
250
257
  reasoning: "No rollback plan provided",
251
258
  },
252
259
  artifactType: "graph-node",
@@ -416,10 +423,17 @@ export class GraphExecutor {
416
423
 
417
424
  try {
418
425
  await client.executeApprovedPlan({
419
- deploymentId: node.deploymentId ?? nodeId,
426
+ operationId: node.deploymentId ?? nodeId,
420
427
  plan,
421
428
  rollbackPlan: {
422
- steps: [],
429
+ scriptedPlan: {
430
+ platform: "bash",
431
+ executionScript: "# Rollback of rollback not supported",
432
+ dryRunScript: null,
433
+ rollbackScript: null,
434
+ reasoning: "Rollback of rollback not supported",
435
+ stepSummary: [],
436
+ },
423
437
  reasoning: "Rollback of rollback not supported",
424
438
  },
425
439
  artifactType: "graph-node-rollback",