@synth-deploy/server 0.1.0 → 1.1.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 (96) hide show
  1. package/dist/agent/envoy-client.d.ts +62 -7
  2. package/dist/agent/envoy-client.d.ts.map +1 -1
  3. package/dist/agent/envoy-client.js +56 -6
  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 +42 -39
  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 +1883 -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 +194 -10
  39. package/dist/api/schemas.d.ts.map +1 -1
  40. package/dist/api/schemas.js +38 -5
  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 +1 -1
  48. package/dist/fleet/fleet-executor.js.map +1 -1
  49. package/dist/graph/graph-executor.js +2 -2
  50. package/dist/graph/graph-executor.js.map +1 -1
  51. package/dist/index.js +44 -40
  52. package/dist/index.js.map +1 -1
  53. package/dist/mcp/resources.js +3 -3
  54. package/dist/mcp/resources.js.map +1 -1
  55. package/dist/mcp/tools.d.ts.map +1 -1
  56. package/dist/mcp/tools.js +2 -9
  57. package/dist/mcp/tools.js.map +1 -1
  58. package/dist/middleware/auth.js +1 -1
  59. package/dist/middleware/auth.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/agent/envoy-client.ts +107 -15
  62. package/src/agent/stale-deployment-detector.ts +1 -1
  63. package/src/agent/synth-agent.ts +59 -45
  64. package/src/alert-webhooks/alert-parsers.ts +291 -0
  65. package/src/api/agent.ts +9 -528
  66. package/src/api/alert-webhooks.ts +354 -0
  67. package/src/api/envoy-reports.ts +2 -2
  68. package/src/api/envoys.ts +1 -1
  69. package/src/api/fleet.ts +14 -15
  70. package/src/api/graph.ts +3 -3
  71. package/src/api/operations.ts +2240 -0
  72. package/src/api/partitions.ts +1 -1
  73. package/src/api/schemas.ts +43 -7
  74. package/src/api/system.ts +23 -21
  75. package/src/artifact-analyzer.ts +2 -2
  76. package/src/fleet/fleet-executor.ts +1 -1
  77. package/src/graph/graph-executor.ts +2 -2
  78. package/src/index.ts +46 -40
  79. package/src/mcp/resources.ts +3 -3
  80. package/src/mcp/tools.ts +5 -9
  81. package/src/middleware/auth.ts +1 -1
  82. package/tests/agent-mode.test.ts +5 -376
  83. package/tests/api-handlers.test.ts +27 -27
  84. package/tests/composite-operations.test.ts +557 -0
  85. package/tests/decision-diary.test.ts +62 -63
  86. package/tests/diary-reader.test.ts +14 -18
  87. package/tests/mcp-tools.test.ts +1 -1
  88. package/tests/orchestration.test.ts +34 -30
  89. package/tests/partition-isolation.test.ts +4 -9
  90. package/tests/rbac-enforcement.test.ts +8 -8
  91. package/tests/ui-journey.test.ts +9 -9
  92. package/dist/api/deployments.d.ts +0 -11
  93. package/dist/api/deployments.d.ts.map +0 -1
  94. package/dist/api/deployments.js +0 -1098
  95. package/dist/api/deployments.js.map +0 -1
  96. package/src/api/deployments.ts +0 -1347
@@ -1,6 +1,6 @@
1
1
  import { SignJWT, jwtVerify } from "jose";
2
2
  const EXEMPT_ROUTES = ["/health", "/api/health", "/api/auth/login", "/api/auth/register", "/api/auth/refresh", "/api/auth/status", "/api/auth/providers", "/api/envoy/report"];
3
- const EXEMPT_PREFIXES = ["/api/auth/oidc/", "/api/auth/callback/oidc/", "/api/auth/saml/", "/api/auth/callback/saml/", "/api/auth/ldap/", "/api/intake/webhook/"];
3
+ const EXEMPT_PREFIXES = ["/api/auth/oidc/", "/api/auth/callback/oidc/", "/api/auth/saml/", "/api/auth/callback/saml/", "/api/auth/ldap/", "/api/intake/webhook/", "/api/alert-webhooks/receive/"];
4
4
  // Envoy callback endpoints — validated by envoy token, not user JWT
5
5
  const EXEMPT_PATTERNS = [/^\/api\/deployments\/[^/]+\/progress$/];
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synth-deploy/server",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "license": "BUSL-1.1",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -3,7 +3,7 @@ import type {
3
3
  HealthCheckResult,
4
4
  } from "./health-checker.js";
5
5
  import { serverLog, serverWarn, serverError } from "../logger.js";
6
- import type { DecisionType, DeploymentPlan, PlannedStep, SecurityBoundary } from "@synth-deploy/core";
6
+ import type { DecisionType, DeploymentPlan, PlannedStep, SecurityBoundary, MonitoringDirective } from "@synth-deploy/core";
7
7
 
8
8
  // ---------------------------------------------------------------------------
9
9
  // Types — Envoy API responses
@@ -11,6 +11,7 @@ import type { DecisionType, DeploymentPlan, PlannedStep, SecurityBoundary } from
11
11
 
12
12
  export interface EnvoyDeployResult {
13
13
  deploymentId: string;
14
+ operationId: string;
14
15
  success: boolean;
15
16
  workspacePath: string;
16
17
  artifacts: string[];
@@ -25,7 +26,7 @@ export interface EnvoyDeployResult {
25
26
  id: string;
26
27
  timestamp: string;
27
28
  partitionId: string | null;
28
- deploymentId: string | null;
29
+ operationId: string | null;
29
30
  agent: "server" | "envoy";
30
31
  decisionType: DecisionType;
31
32
  decision: string;
@@ -176,9 +177,9 @@ export class EnvoyClient {
176
177
  */
177
178
  async deploy(instruction: {
178
179
  deploymentId: string;
180
+ operationId: string;
179
181
  partitionId: string;
180
182
  environmentId: string;
181
- operationId: string;
182
183
  version: string;
183
184
  variables: Record<string, string>;
184
185
  environmentName: string;
@@ -202,7 +203,7 @@ export class EnvoyClient {
202
203
  * Dispatch an approved plan to the Envoy for deterministic execution.
203
204
  */
204
205
  async executeApprovedPlan(params: {
205
- deploymentId: string;
206
+ operationId: string;
206
207
  plan: DeploymentPlan;
207
208
  rollbackPlan: DeploymentPlan;
208
209
  artifactType: string;
@@ -211,7 +212,7 @@ export class EnvoyClient {
211
212
  progressCallbackUrl?: string;
212
213
  callbackToken?: string;
213
214
  }): Promise<EnvoyDeployResult> {
214
- serverLog("ENVOY-EXECUTE", { deploymentId: params.deploymentId, envoyUrl: this.baseUrl, artifact: params.artifactName, steps: params.plan?.steps?.length ?? 0 });
215
+ serverLog("ENVOY-EXECUTE", { operationId: params.operationId, envoyUrl: this.baseUrl, artifact: params.artifactName, steps: params.plan?.steps?.length ?? 0 });
215
216
  const execStart = Date.now();
216
217
  const response = await fetchWithRetry(
217
218
  `${this.baseUrl}/execute`,
@@ -225,9 +226,9 @@ export class EnvoyClient {
225
226
 
226
227
  const execResult = (await response.json()) as EnvoyDeployResult;
227
228
  if (execResult.success) {
228
- serverLog("ENVOY-EXECUTE-COMPLETE", { deploymentId: params.deploymentId, durationMs: Date.now() - execStart });
229
+ serverLog("ENVOY-EXECUTE-COMPLETE", { operationId: params.operationId, durationMs: Date.now() - execStart });
229
230
  } else {
230
- serverLog("ENVOY-EXECUTE-FAILED", { deploymentId: params.deploymentId, failureReason: execResult.failureReason, durationMs: Date.now() - execStart });
231
+ serverLog("ENVOY-EXECUTE-FAILED", { operationId: params.operationId, failureReason: execResult.failureReason, durationMs: Date.now() - execStart });
231
232
  }
232
233
  return execResult;
233
234
  }
@@ -238,8 +239,13 @@ export class EnvoyClient {
238
239
  * POST /api/deployments/:id/plan to move the deployment to awaiting_approval.
239
240
  */
240
241
  async requestPlan(params: {
241
- deploymentId: string;
242
- artifact: {
242
+ operationId: string;
243
+ operationType?: "deploy" | "query" | "investigate" | "maintain" | "trigger";
244
+ intent?: string;
245
+ allowWrite?: boolean;
246
+ triggerCondition?: string;
247
+ triggerResponseIntent?: string;
248
+ artifact?: {
243
249
  id: string;
244
250
  name: string;
245
251
  type: string;
@@ -264,10 +270,21 @@ export class EnvoyClient {
264
270
  version: string;
265
271
  resolvedVariables: Record<string, string>;
266
272
  refinementFeedback?: string;
267
- }): Promise<{ plan: DeploymentPlan; rollbackPlan: DeploymentPlan; delta?: string; assessmentSummary?: string; blocked?: boolean; blockReason?: string }> {
273
+ }): Promise<{
274
+ plan: DeploymentPlan;
275
+ rollbackPlan: DeploymentPlan;
276
+ delta?: string;
277
+ assessmentSummary?: string;
278
+ blocked?: boolean;
279
+ blockReason?: string;
280
+ queryFindings?: { targetsSurveyed: string[]; summary: string; findings: Array<{ target: string; observations: string[] }> };
281
+ investigationFindings?: { targetsSurveyed: string[]; summary: string; findings: Array<{ target: string; observations: string[] }>; rootCause?: string; proposedResolution?: { intent: string; operationType: "deploy" | "maintain"; parameters?: Record<string, unknown> } };
282
+ intervalMs?: number;
283
+ cooldownMs?: number;
284
+ }> {
268
285
  // Forward the LLM API key so the Envoy can use it if it started without one.
269
286
  // Sent in the request body (not headers) over the trusted server↔envoy channel.
270
- serverLog("ENVOY-PLAN-REQUEST", { deploymentId: params.deploymentId, envoyUrl: this.baseUrl, artifact: params.artifact.name, version: params.version, environment: params.environment.name });
287
+ serverLog("ENVOY-PLAN-REQUEST", { operationId: params.operationId, envoyUrl: this.baseUrl, artifact: params.artifact?.name, version: params.version, environment: params.environment.name });
271
288
  const llmApiKey = process.env.SYNTH_LLM_API_KEY;
272
289
  const planStart = Date.now();
273
290
  const response = await fetchWithRetry(
@@ -282,12 +299,12 @@ export class EnvoyClient {
282
299
 
283
300
  if (!response.ok) {
284
301
  const body = await response.text().catch(() => "");
285
- serverLog("ENVOY-PLAN-FAILED", { deploymentId: params.deploymentId, status: response.status, durationMs: Date.now() - planStart });
302
+ serverLog("ENVOY-PLAN-FAILED", { operationId: params.operationId, status: response.status, durationMs: Date.now() - planStart });
286
303
  throw new Error(`Envoy planning failed (HTTP ${response.status}): ${body}`);
287
304
  }
288
305
 
289
- const planResult = (await response.json()) as { plan: DeploymentPlan; rollbackPlan: DeploymentPlan; delta?: string; assessmentSummary?: string; blocked?: boolean; blockReason?: string };
290
- serverLog("ENVOY-PLAN-RECEIVED", { deploymentId: params.deploymentId, steps: planResult.plan?.steps?.length ?? 0, blocked: planResult.blocked ?? false, durationMs: Date.now() - planStart });
306
+ const planResult = (await response.json()) as Awaited<ReturnType<typeof this.requestPlan>>;
307
+ serverLog("ENVOY-PLAN-RECEIVED", { operationId: params.operationId, steps: planResult.plan?.steps?.length ?? 0, blocked: planResult.blocked ?? false, durationMs: Date.now() - planStart });
291
308
  return planResult;
292
309
  }
293
310
 
@@ -297,7 +314,7 @@ export class EnvoyClient {
297
314
  * than the forward plan, so the rollback is targeted to what actually changed.
298
315
  */
299
316
  async requestRollbackPlan(params: {
300
- deploymentId: string;
317
+ operationId: string;
301
318
  artifact: {
302
319
  name: string;
303
320
  type: string;
@@ -381,6 +398,81 @@ export class EnvoyClient {
381
398
  );
382
399
  return (await response.json()) as { mode: "replan" | "rejection" | "response"; message: string };
383
400
  }
401
+
402
+ /**
403
+ * Install a monitoring directive on the Envoy for health-based triggering.
404
+ */
405
+ async installMonitoringDirective(directive: MonitoringDirective): Promise<void> {
406
+ serverLog("ENVOY-MONITOR-INSTALL", { directiveId: directive.id, envoyUrl: this.baseUrl });
407
+ const response = await fetchWithRetry(
408
+ `${this.baseUrl}/monitor`,
409
+ {
410
+ method: "POST",
411
+ headers: { "Content-Type": "application/json" },
412
+ body: JSON.stringify(directive),
413
+ },
414
+ this.timeoutMs,
415
+ );
416
+ if (!response.ok) {
417
+ const body = await response.text().catch(() => "");
418
+ throw new Error(`Failed to install monitoring directive (HTTP ${response.status}): ${body}`);
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Pause a monitoring directive on the Envoy.
424
+ */
425
+ async pauseMonitoringDirective(directiveId: string): Promise<void> {
426
+ const response = await fetchWithRetry(
427
+ `${this.baseUrl}/monitor/${directiveId}/pause`,
428
+ { method: "POST", headers: { "Content-Type": "application/json" } },
429
+ this.timeoutMs,
430
+ );
431
+ if (!response.ok) {
432
+ throw new Error(`Failed to pause monitoring directive (HTTP ${response.status})`);
433
+ }
434
+ }
435
+
436
+ /**
437
+ * Resume a monitoring directive on the Envoy.
438
+ */
439
+ async resumeMonitoringDirective(directiveId: string): Promise<void> {
440
+ const response = await fetchWithRetry(
441
+ `${this.baseUrl}/monitor/${directiveId}/resume`,
442
+ { method: "POST", headers: { "Content-Type": "application/json" } },
443
+ this.timeoutMs,
444
+ );
445
+ if (!response.ok) {
446
+ throw new Error(`Failed to resume monitoring directive (HTTP ${response.status})`);
447
+ }
448
+ }
449
+
450
+ /**
451
+ * Remove a monitoring directive from the Envoy.
452
+ */
453
+ async removeMonitoringDirective(directiveId: string): Promise<void> {
454
+ const response = await fetchWithRetry(
455
+ `${this.baseUrl}/monitor/${directiveId}`,
456
+ { method: "DELETE", headers: { "Content-Type": "application/json" } },
457
+ this.timeoutMs,
458
+ );
459
+ if (!response.ok) {
460
+ throw new Error(`Failed to remove monitoring directive (HTTP ${response.status})`);
461
+ }
462
+ }
463
+
464
+ /**
465
+ * List active monitoring directives on the Envoy.
466
+ */
467
+ async listMonitoringDirectives(): Promise<Array<{ directive: MonitoringDirective; lastFiredAt: number; fireCount: number; suppressedCount: number }>> {
468
+ const response = await fetchWithRetry(
469
+ `${this.baseUrl}/monitor`,
470
+ {},
471
+ this.timeoutMs,
472
+ );
473
+ const data = (await response.json()) as { directives: Array<{ directive: MonitoringDirective; lastFiredAt: number; fireCount: number; suppressedCount: number }> };
474
+ return data.directives;
475
+ }
384
476
  }
385
477
 
386
478
  // ---------------------------------------------------------------------------
@@ -37,7 +37,7 @@ export function markStaleDeployments(
37
37
 
38
38
  debrief.record({
39
39
  partitionId: deployment.partitionId ?? null,
40
- deploymentId: deployment.id,
40
+ operationId: deployment.id,
41
41
  agent: "server",
42
42
  decisionType: "deployment-failure",
43
43
  decision: `Marked deployment as failed: exceeded ${Math.round(thresholdMs / 60_000)} minute stale threshold`,
@@ -2,7 +2,8 @@ import crypto from "node:crypto";
2
2
  import type {
3
3
  Deployment,
4
4
  DeploymentId,
5
- DeploymentTrigger,
5
+ OperationInput,
6
+ OperationTrigger,
6
7
  DebriefWriter,
7
8
  Environment,
8
9
  Partition,
@@ -25,7 +26,7 @@ import { serverLog, serverError } from "../logger.js";
25
26
  // Public interfaces
26
27
  // ---------------------------------------------------------------------------
27
28
 
28
- export interface DeploymentStore {
29
+ export interface OperationStore {
29
30
  save(deployment: Deployment): void;
30
31
  get(id: DeploymentId): Deployment | undefined;
31
32
  getByPartition(partitionId: string): Deployment[];
@@ -36,6 +37,8 @@ export interface DeploymentStore {
36
37
  findRecentByArtifact(artifactId: string, since: Date, status?: string): Deployment[];
37
38
  findLatestByEnvironment(envId: string): Deployment | undefined;
38
39
  }
40
+ /** @deprecated Use OperationStore */
41
+ export type DeploymentStore = OperationStore;
39
42
 
40
43
  export interface AgentOptions {
41
44
  /** Number of health check retries after initial failure. Default: 1 */
@@ -172,7 +175,7 @@ export class SynthAgent {
172
175
 
173
176
  constructor(
174
177
  private debrief: DebriefWriter,
175
- private deployments: DeploymentStore,
178
+ private deployments: OperationStore,
176
179
  private artifactStore: IArtifactStore,
177
180
  private environmentStore: IEnvironmentStore,
178
181
  private partitionStore: IPartitionStore,
@@ -217,14 +220,23 @@ export class SynthAgent {
217
220
  // Main entry point
218
221
  // -----------------------------------------------------------------------
219
222
 
220
- async triggerDeployment(
221
- trigger: DeploymentTrigger,
223
+ async triggerOperation(
224
+ input: OperationInput,
225
+ trigger: OperationTrigger,
222
226
  ): Promise<Deployment> {
227
+ if (input.type !== "deploy") {
228
+ throw new OrchestrationError(
229
+ "resolve-entities",
230
+ `Operation type "${input.type}" is not supported via programmatic triggers`,
231
+ `"query" and "investigate" operations run through the REST API path (web UI), not programmatic triggers. Only "deploy" is supported via triggerOperation().`,
232
+ );
233
+ }
234
+
223
235
  const deploymentId = crypto.randomUUID();
224
236
 
225
237
  // --- Look up entities from stores -----------------------------------------------
226
238
 
227
- serverLog("DEPLOY-TRIGGER", { deploymentId, artifactId: trigger.artifactId, environmentId: trigger.environmentId, partitionId: trigger.partitionId ?? null, triggeredBy: trigger.triggeredBy });
239
+ serverLog("DEPLOY-TRIGGER", { operationId: deploymentId, artifactId: input.artifactId, environmentId: trigger.environmentId, partitionId: trigger.partitionId ?? null, triggeredBy: trigger.triggeredBy });
228
240
 
229
241
  const environment = this.environmentStore.get(trigger.environmentId);
230
242
  if (!environment) {
@@ -249,12 +261,12 @@ export class SynthAgent {
249
261
  }
250
262
  }
251
263
 
252
- const artifact = this.artifactStore.get(trigger.artifactId);
264
+ const artifact = this.artifactStore.get(input.artifactId);
253
265
  if (!artifact) {
254
266
  throw new OrchestrationError(
255
267
  "resolve-entities",
256
- `Artifact not found: ${trigger.artifactId}`,
257
- `The trigger references artifact ID "${trigger.artifactId}" which does not exist in the artifact store. ` +
268
+ `Artifact not found: ${input.artifactId}`,
269
+ `The trigger references artifact ID "${input.artifactId}" which does not exist in the artifact store. ` +
258
270
  `Verify the artifact ID is correct and the artifact has been created.`,
259
271
  );
260
272
  }
@@ -275,7 +287,7 @@ export class SynthAgent {
275
287
 
276
288
  const analysisEntry = this.debrief.record({
277
289
  partitionId: trigger.partitionId ?? null,
278
- deploymentId,
290
+ operationId: deploymentId,
279
291
  agent: "server",
280
292
  decisionType: "artifact-analysis",
281
293
  decision: `Analyzed artifact "${artifact.name}" (${artifact.type}) — confidence ${artifact.analysis.confidence}`,
@@ -299,7 +311,7 @@ export class SynthAgent {
299
311
 
300
312
  // --- Step 1: Plan the pipeline -----------------------------------------
301
313
 
302
- const version = trigger.artifactVersionId ?? "latest";
314
+ const version = input.artifactVersionId ?? "latest";
303
315
  const pipelineSteps = [
304
316
  "resolve-configuration",
305
317
  "preflight-health-check",
@@ -309,7 +321,7 @@ export class SynthAgent {
309
321
 
310
322
  const planEntry = this.debrief.record({
311
323
  partitionId: trigger.partitionId ?? null,
312
- deploymentId,
324
+ operationId: deploymentId,
313
325
  agent: "server",
314
326
  decisionType: "pipeline-plan",
315
327
  decision: `Planned deployment pipeline: ${pipelineSteps.join(" → ")}`,
@@ -349,7 +361,7 @@ export class SynthAgent {
349
361
 
350
362
  const planGenEntry = this.debrief.record({
351
363
  partitionId: trigger.partitionId ?? null,
352
- deploymentId,
364
+ operationId: deploymentId,
353
365
  agent: "server",
354
366
  decisionType: "plan-generation",
355
367
  decision: `Generated deployment plan for "${artifact.name}" v${version} — ${initialPlan.steps.length} step(s)`,
@@ -363,7 +375,7 @@ export class SynthAgent {
363
375
 
364
376
  const approvalEntry = this.debrief.record({
365
377
  partitionId: trigger.partitionId ?? null,
366
- deploymentId,
378
+ operationId: deploymentId,
367
379
  agent: "server",
368
380
  decisionType: "plan-approval",
369
381
  decision: `Auto-approved deployment plan for "${artifact.name}" v${version}`,
@@ -378,8 +390,7 @@ export class SynthAgent {
378
390
 
379
391
  const deployment: Deployment = {
380
392
  id: deploymentId,
381
- artifactId: trigger.artifactId,
382
- artifactVersionId: trigger.artifactVersionId,
393
+ input,
383
394
  environmentId: trigger.environmentId,
384
395
  partitionId: trigger.partitionId,
385
396
  version,
@@ -397,7 +408,7 @@ export class SynthAgent {
397
408
  try {
398
409
  // --- Step 2: Resolve configuration -----------------------------------
399
410
 
400
- serverLog("DEPLOY-CONFIG-RESOLVE", { deploymentId: deployment.id });
411
+ serverLog("DEPLOY-CONFIG-RESOLVE", { operationId: deployment.id });
401
412
  const { variables, hasConflicts } = this.resolveConfiguration(
402
413
  deployment,
403
414
  trigger.variables,
@@ -411,13 +422,13 @@ export class SynthAgent {
411
422
 
412
423
  deployment.status = "running";
413
424
  this.deployments.save(deployment);
414
- serverLog("DEPLOY-HEALTH-CHECK", { deploymentId: deployment.id, environment: environment.name });
425
+ serverLog("DEPLOY-HEALTH-CHECK", { operationId: deployment.id, environment: environment.name });
415
426
 
416
427
  await this.preflightHealthCheck(deployment, partition, environment, artifact);
417
428
 
418
429
  // --- Step 4: Execute deployment ----------------------------------------
419
430
 
420
- serverLog("DEPLOY-EXECUTE", { deploymentId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name });
431
+ serverLog("DEPLOY-EXECUTE", { operationId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name });
421
432
  const delegated = await this.executeDeployment(deployment, partition, environment, artifact);
422
433
 
423
434
  // --- Step 5: Post-deploy verify ----------------------------------------
@@ -433,11 +444,11 @@ export class SynthAgent {
433
444
 
434
445
  deployment.status = "succeeded";
435
446
  deployment.completedAt = new Date();
436
- serverLog("DEPLOY-SUCCEEDED", { deploymentId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
447
+ serverLog("DEPLOY-SUCCEEDED", { operationId: deployment.id, artifact: artifact.name, version: deployment.version, environment: environment.name, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
437
448
 
438
449
  const completionEntry = this.debrief.record({
439
450
  partitionId: deployment.partitionId ?? null,
440
- deploymentId: deployment.id,
451
+ operationId: deployment.id,
441
452
  agent: "server",
442
453
  decisionType: "deployment-completion",
443
454
  decision: `Marking deployment of ${artifact.name} v${deployment.version} as succeeded on "${environment.name}"`,
@@ -465,11 +476,11 @@ export class SynthAgent {
465
476
  error instanceof OrchestrationError
466
477
  ? error.message
467
478
  : `Unexpected error: ${error instanceof Error ? error.message : String(error)}`;
468
- serverError("DEPLOY-FAILED", { deploymentId: deployment.id, reason: deployment.failureReason, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
479
+ serverError("DEPLOY-FAILED", { operationId: deployment.id, reason: deployment.failureReason, durationMs: deployment.completedAt.getTime() - deployment.createdAt.getTime() });
469
480
 
470
481
  const failEntry = this.debrief.record({
471
482
  partitionId: deployment.partitionId ?? null,
472
- deploymentId: deployment.id,
483
+ operationId: deployment.id,
473
484
  agent: "server",
474
485
  decisionType: "deployment-failure",
475
486
  decision: `Deployment failed: ${deployment.failureReason}`,
@@ -532,7 +543,7 @@ export class SynthAgent {
532
543
 
533
544
  this.debrief.record({
534
545
  partitionId,
535
- deploymentId: null,
546
+ operationId: null,
536
547
  agent: "server",
537
548
  decisionType: "diagnostic-investigation",
538
549
  decision: `${tools.length} external tool(s) available from ${connectedServers.length} MCP server(s)`,
@@ -557,7 +568,7 @@ export class SynthAgent {
557
568
  // Never let external checks block the deployment
558
569
  this.debrief.record({
559
570
  partitionId,
560
- deploymentId: null,
571
+ operationId: null,
561
572
  agent: "server",
562
573
  decisionType: "diagnostic-investigation",
563
574
  decision: "External MCP check failed — proceeding without external intelligence",
@@ -648,7 +659,7 @@ export class SynthAgent {
648
659
  const partitionVarCount = partition ? Object.keys(partition.variables).length : 0;
649
660
  const configEntry = this.debrief.record({
650
661
  partitionId: deployment.partitionId ?? null,
651
- deploymentId: deployment.id,
662
+ operationId: deployment.id,
652
663
  agent: "server",
653
664
  decisionType: "configuration-resolved",
654
665
  decision:
@@ -828,7 +839,7 @@ export class SynthAgent {
828
839
  if (crossEnvConn) {
829
840
  const entry = this.debrief.record({
830
841
  partitionId: deployment.partitionId ?? null,
831
- deploymentId: deployment.id,
842
+ operationId: deployment.id,
832
843
  agent: "server",
833
844
  decisionType: "variable-conflict",
834
845
  decision:
@@ -859,7 +870,7 @@ export class SynthAgent {
859
870
  .join("; ");
860
871
  const entry = this.debrief.record({
861
872
  partitionId: deployment.partitionId ?? null,
862
- deploymentId: deployment.id,
873
+ operationId: deployment.id,
863
874
  agent: "server",
864
875
  decisionType: "variable-conflict",
865
876
  decision: `Cross-environment variable pattern in ${crossEnv.length} non-connectivity variable(s)`,
@@ -884,7 +895,7 @@ export class SynthAgent {
884
895
  if (sensitiveDetails) {
885
896
  const entry = this.debrief.record({
886
897
  partitionId: deployment.partitionId ?? null,
887
- deploymentId: deployment.id,
898
+ operationId: deployment.id,
888
899
  agent: "server",
889
900
  decisionType: "variable-conflict",
890
901
  decision: `Security-sensitive variable(s) overridden: ${sensitiveDetails.map((d) => d.conflict.variable).join(", ")}`,
@@ -919,7 +930,7 @@ export class SynthAgent {
919
930
  .join("; ");
920
931
  const entry = this.debrief.record({
921
932
  partitionId: deployment.partitionId ?? null,
922
- deploymentId: deployment.id,
933
+ operationId: deployment.id,
923
934
  agent: "server",
924
935
  decisionType: "variable-conflict",
925
936
  decision: `Resolved ${standardDetails.length} variable conflict(s) via precedence rules`,
@@ -993,7 +1004,7 @@ export class SynthAgent {
993
1004
  if (firstCheck.reachable) {
994
1005
  const entry = this.debrief.record({
995
1006
  partitionId: deployment.partitionId ?? null,
996
- deploymentId: deployment.id,
1007
+ operationId: deployment.id,
997
1008
  agent: "server",
998
1009
  decisionType: "health-check",
999
1010
  decision: `Proceeding with deployment — target environment "${environment.name}" confirmed healthy in ${firstCheck.responseTimeMs}ms`,
@@ -1025,7 +1036,7 @@ export class SynthAgent {
1025
1036
  // Reasoning determined retrying won't help (e.g., DNS failure)
1026
1037
  const abortEntry = this.debrief.record({
1027
1038
  partitionId: deployment.partitionId ?? null,
1028
- deploymentId: deployment.id,
1039
+ operationId: deployment.id,
1029
1040
  agent: "server",
1030
1041
  decisionType: "health-check",
1031
1042
  decision: "Pre-flight health check failed — aborting without retry",
@@ -1050,7 +1061,7 @@ export class SynthAgent {
1050
1061
  // Decision is to retry
1051
1062
  const retryEntry = this.debrief.record({
1052
1063
  partitionId: deployment.partitionId ?? null,
1053
- deploymentId: deployment.id,
1064
+ operationId: deployment.id,
1054
1065
  agent: "server",
1055
1066
  decisionType: "health-check",
1056
1067
  decision: "Pre-flight health check failed — attempting retry",
@@ -1079,7 +1090,7 @@ export class SynthAgent {
1079
1090
  if (retryCheck.reachable) {
1080
1091
  const recoveryEntry = this.debrief.record({
1081
1092
  partitionId: deployment.partitionId ?? null,
1082
- deploymentId: deployment.id,
1093
+ operationId: deployment.id,
1083
1094
  agent: "server",
1084
1095
  decisionType: "health-check",
1085
1096
  decision:
@@ -1297,7 +1308,7 @@ export class SynthAgent {
1297
1308
  // No settingsReader configured — execution skipped (test/offline environment)
1298
1309
  const execEntry = this.debrief.record({
1299
1310
  partitionId: deployment.partitionId ?? null,
1300
- deploymentId: deployment.id,
1311
+ operationId: deployment.id,
1301
1312
  agent: "server",
1302
1313
  decisionType: "deployment-execution",
1303
1314
  decision: `Skipped Envoy delegation for ${artifact.name} v${deployment.version} — no settings reader configured`,
@@ -1355,7 +1366,7 @@ export class SynthAgent {
1355
1366
  // Envoy is healthy — delegate the deployment
1356
1367
  const delegateEntry = this.debrief.record({
1357
1368
  partitionId: deployment.partitionId ?? null,
1358
- deploymentId: deployment.id,
1369
+ operationId: deployment.id,
1359
1370
  agent: "server",
1360
1371
  decisionType: "deployment-execution",
1361
1372
  decision: `Delegating execution of ${artifact.name} v${deployment.version} to Envoy at ${envoyConfig.url}`,
@@ -1382,10 +1393,10 @@ export class SynthAgent {
1382
1393
 
1383
1394
  envoyResult = await client.deploy({
1384
1395
  deploymentId: deployment.id,
1396
+ operationId: deployment.id,
1385
1397
  partitionId: deployment.partitionId ?? "",
1386
1398
  environmentId: deployment.environmentId ?? "",
1387
- operationId: deployment.artifactId, // Envoy still uses operationId in its API
1388
- version: deployment.version,
1399
+ version: deployment.version ?? "",
1389
1400
  variables: deployment.variables,
1390
1401
  environmentName: environment.name,
1391
1402
  partitionName: partition?.name ?? "",
@@ -1407,7 +1418,7 @@ export class SynthAgent {
1407
1418
  for (const entry of envoyResult.debriefEntries) {
1408
1419
  const ingested = this.debrief.record({
1409
1420
  partitionId: entry.partitionId ?? deployment.partitionId ?? null,
1410
- deploymentId: entry.deploymentId ?? deployment.id,
1421
+ operationId: entry.operationId ?? deployment.id,
1411
1422
  agent: entry.agent,
1412
1423
  decisionType: entry.decisionType,
1413
1424
  decision: entry.decision,
@@ -1442,7 +1453,7 @@ export class SynthAgent {
1442
1453
  // Record successful delegation completion
1443
1454
  const completionEntry = this.debrief.record({
1444
1455
  partitionId: deployment.partitionId ?? null,
1445
- deploymentId: deployment.id,
1456
+ operationId: deployment.id,
1446
1457
  agent: "server",
1447
1458
  decisionType: "deployment-execution",
1448
1459
  decision: `Envoy completed deployment successfully in ${envoyResult.executionDurationMs}ms — ${envoyResult.artifacts.length} artifact(s) produced`,
@@ -1475,7 +1486,7 @@ export class SynthAgent {
1475
1486
  ): Promise<void> {
1476
1487
  const entry = this.debrief.record({
1477
1488
  partitionId: deployment.partitionId ?? null,
1478
- deploymentId: deployment.id,
1489
+ operationId: deployment.id,
1479
1490
  agent: "server",
1480
1491
  decisionType: "deployment-verification",
1481
1492
  decision: `Post-deploy verification skipped for ${artifact.name} v${deployment.version} — no Envoy configured`,
@@ -1506,7 +1517,10 @@ export class SynthAgent {
1506
1517
  // In-memory deployment store
1507
1518
  // ---------------------------------------------------------------------------
1508
1519
 
1509
- export class InMemoryDeploymentStore implements DeploymentStore {
1520
+ const getArtifactId = (op: Deployment): string =>
1521
+ op.input?.type === "deploy" ? (op.input as { type: "deploy"; artifactId: string }).artifactId : "";
1522
+
1523
+ export class InMemoryDeploymentStore implements OperationStore {
1510
1524
  private deployments: Map<DeploymentId, Deployment> = new Map();
1511
1525
 
1512
1526
  save(deployment: Deployment): void {
@@ -1525,7 +1539,7 @@ export class InMemoryDeploymentStore implements DeploymentStore {
1525
1539
 
1526
1540
  getByArtifact(artifactId: string): Deployment[] {
1527
1541
  return [...this.deployments.values()].filter(
1528
- (d) => d.artifactId === artifactId,
1542
+ (d) => getArtifactId(d) === artifactId,
1529
1543
  );
1530
1544
  }
1531
1545
 
@@ -1542,7 +1556,7 @@ export class InMemoryDeploymentStore implements DeploymentStore {
1542
1556
  findByArtifactVersion(artifactId: string, version: string, status?: string): Deployment[] {
1543
1557
  return [...this.deployments.values()].filter(
1544
1558
  (d) =>
1545
- d.artifactId === artifactId &&
1559
+ getArtifactId(d) === artifactId &&
1546
1560
  d.version === version &&
1547
1561
  (!status || d.status === status),
1548
1562
  );
@@ -1552,7 +1566,7 @@ export class InMemoryDeploymentStore implements DeploymentStore {
1552
1566
  return [...this.deployments.values()]
1553
1567
  .filter(
1554
1568
  (d) =>
1555
- d.artifactId === artifactId &&
1569
+ getArtifactId(d) === artifactId &&
1556
1570
  new Date(d.createdAt).getTime() >= since.getTime() &&
1557
1571
  (!status || d.status === status),
1558
1572
  )