agentic-qe 3.4.2 → 3.4.4

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 (119) hide show
  1. package/.claude/skills/README.md +126 -35
  2. package/package.json +1 -1
  3. package/v3/CHANGELOG.md +50 -0
  4. package/v3/README.md +18 -24
  5. package/v3/dist/cli/bundle.js +10693 -10285
  6. package/v3/dist/cli/commands/init.d.ts.map +1 -1
  7. package/v3/dist/cli/commands/init.js +5 -2
  8. package/v3/dist/cli/commands/init.js.map +1 -1
  9. package/v3/dist/cli/handlers/init-handler.d.ts +1 -0
  10. package/v3/dist/cli/handlers/init-handler.d.ts.map +1 -1
  11. package/v3/dist/cli/handlers/init-handler.js +10 -1
  12. package/v3/dist/cli/handlers/init-handler.js.map +1 -1
  13. package/v3/dist/init/agents-installer.d.ts.map +1 -1
  14. package/v3/dist/init/agents-installer.js +20 -16
  15. package/v3/dist/init/agents-installer.js.map +1 -1
  16. package/v3/dist/init/orchestrator.d.ts.map +1 -1
  17. package/v3/dist/init/orchestrator.js +1 -0
  18. package/v3/dist/init/orchestrator.js.map +1 -1
  19. package/v3/dist/init/phases/04-database.d.ts +14 -2
  20. package/v3/dist/init/phases/04-database.d.ts.map +1 -1
  21. package/v3/dist/init/phases/04-database.js +50 -48
  22. package/v3/dist/init/phases/04-database.js.map +1 -1
  23. package/v3/dist/init/phases/08-mcp.d.ts +6 -1
  24. package/v3/dist/init/phases/08-mcp.d.ts.map +1 -1
  25. package/v3/dist/init/phases/08-mcp.js +54 -28
  26. package/v3/dist/init/phases/08-mcp.js.map +1 -1
  27. package/v3/dist/init/phases/09-assets.d.ts.map +1 -1
  28. package/v3/dist/init/phases/09-assets.js +8 -3
  29. package/v3/dist/init/phases/09-assets.js.map +1 -1
  30. package/v3/dist/init/phases/10-workers.d.ts +11 -0
  31. package/v3/dist/init/phases/10-workers.d.ts.map +1 -1
  32. package/v3/dist/init/phases/10-workers.js +246 -11
  33. package/v3/dist/init/phases/10-workers.js.map +1 -1
  34. package/v3/dist/init/phases/phase-interface.d.ts +2 -0
  35. package/v3/dist/init/phases/phase-interface.d.ts.map +1 -1
  36. package/v3/dist/init/phases/phase-interface.js.map +1 -1
  37. package/v3/dist/init/skills-installer.d.ts +5 -0
  38. package/v3/dist/init/skills-installer.d.ts.map +1 -1
  39. package/v3/dist/init/skills-installer.js +77 -26
  40. package/v3/dist/init/skills-installer.js.map +1 -1
  41. package/v3/dist/integrations/agent-booster-wasm/index.d.ts +36 -19
  42. package/v3/dist/integrations/agent-booster-wasm/index.d.ts.map +1 -1
  43. package/v3/dist/integrations/agent-booster-wasm/index.js +32 -21
  44. package/v3/dist/integrations/agent-booster-wasm/index.js.map +1 -1
  45. package/v3/dist/integrations/agentic-flow/agent-booster/adapter.d.ts.map +1 -1
  46. package/v3/dist/integrations/agentic-flow/agent-booster/adapter.js +57 -4
  47. package/v3/dist/integrations/agentic-flow/agent-booster/adapter.js.map +1 -1
  48. package/v3/dist/mcp/bundle.js +143 -28
  49. package/v3/dist/strange-loop/healing-controller.d.ts.map +1 -1
  50. package/v3/dist/strange-loop/healing-controller.js +54 -2
  51. package/v3/dist/strange-loop/healing-controller.js.map +1 -1
  52. package/v3/dist/strange-loop/index.d.ts +2 -1
  53. package/v3/dist/strange-loop/index.d.ts.map +1 -1
  54. package/v3/dist/strange-loop/index.js +3 -1
  55. package/v3/dist/strange-loop/index.js.map +1 -1
  56. package/v3/dist/strange-loop/infra-healing/composite-action-executor.d.ts +39 -0
  57. package/v3/dist/strange-loop/infra-healing/composite-action-executor.d.ts.map +1 -0
  58. package/v3/dist/strange-loop/infra-healing/composite-action-executor.js +82 -0
  59. package/v3/dist/strange-loop/infra-healing/composite-action-executor.js.map +1 -0
  60. package/v3/dist/strange-loop/infra-healing/coordination-lock.d.ts +54 -0
  61. package/v3/dist/strange-loop/infra-healing/coordination-lock.d.ts.map +1 -0
  62. package/v3/dist/strange-loop/infra-healing/coordination-lock.js +104 -0
  63. package/v3/dist/strange-loop/infra-healing/coordination-lock.js.map +1 -0
  64. package/v3/dist/strange-loop/infra-healing/index.d.ts +20 -0
  65. package/v3/dist/strange-loop/infra-healing/index.d.ts.map +1 -0
  66. package/v3/dist/strange-loop/infra-healing/index.js +26 -0
  67. package/v3/dist/strange-loop/infra-healing/index.js.map +1 -0
  68. package/v3/dist/strange-loop/infra-healing/infra-action-executor.d.ts +70 -0
  69. package/v3/dist/strange-loop/infra-healing/infra-action-executor.d.ts.map +1 -0
  70. package/v3/dist/strange-loop/infra-healing/infra-action-executor.js +260 -0
  71. package/v3/dist/strange-loop/infra-healing/infra-action-executor.js.map +1 -0
  72. package/v3/dist/strange-loop/infra-healing/infra-aware-agent-provider.d.ts +56 -0
  73. package/v3/dist/strange-loop/infra-healing/infra-aware-agent-provider.d.ts.map +1 -0
  74. package/v3/dist/strange-loop/infra-healing/infra-aware-agent-provider.js +108 -0
  75. package/v3/dist/strange-loop/infra-healing/infra-aware-agent-provider.js.map +1 -0
  76. package/v3/dist/strange-loop/infra-healing/infra-healing-orchestrator.d.ts +102 -0
  77. package/v3/dist/strange-loop/infra-healing/infra-healing-orchestrator.d.ts.map +1 -0
  78. package/v3/dist/strange-loop/infra-healing/infra-healing-orchestrator.js +201 -0
  79. package/v3/dist/strange-loop/infra-healing/infra-healing-orchestrator.js.map +1 -0
  80. package/v3/dist/strange-loop/infra-healing/recovery-playbook.d.ts +74 -0
  81. package/v3/dist/strange-loop/infra-healing/recovery-playbook.d.ts.map +1 -0
  82. package/v3/dist/strange-loop/infra-healing/recovery-playbook.js +153 -0
  83. package/v3/dist/strange-loop/infra-healing/recovery-playbook.js.map +1 -0
  84. package/v3/dist/strange-loop/infra-healing/test-output-observer.d.ts +61 -0
  85. package/v3/dist/strange-loop/infra-healing/test-output-observer.d.ts.map +1 -0
  86. package/v3/dist/strange-loop/infra-healing/test-output-observer.js +339 -0
  87. package/v3/dist/strange-loop/infra-healing/test-output-observer.js.map +1 -0
  88. package/v3/dist/strange-loop/infra-healing/types.d.ts +253 -0
  89. package/v3/dist/strange-loop/infra-healing/types.d.ts.map +1 -0
  90. package/v3/dist/strange-loop/infra-healing/types.js +33 -0
  91. package/v3/dist/strange-loop/infra-healing/types.js.map +1 -0
  92. package/v3/dist/strange-loop/strange-loop.d.ts +63 -0
  93. package/v3/dist/strange-loop/strange-loop.d.ts.map +1 -1
  94. package/v3/dist/strange-loop/strange-loop.js +62 -0
  95. package/v3/dist/strange-loop/strange-loop.js.map +1 -1
  96. package/v3/dist/strange-loop/types.d.ts +2 -2
  97. package/v3/dist/strange-loop/types.d.ts.map +1 -1
  98. package/v3/dist/strange-loop/types.js.map +1 -1
  99. package/v3/dist/sync/embeddings/index.d.ts +9 -0
  100. package/v3/dist/sync/embeddings/index.d.ts.map +1 -0
  101. package/v3/dist/sync/embeddings/index.js +9 -0
  102. package/v3/dist/sync/embeddings/index.js.map +1 -0
  103. package/v3/dist/sync/embeddings/sync-embedding-generator.d.ts +101 -0
  104. package/v3/dist/sync/embeddings/sync-embedding-generator.d.ts.map +1 -0
  105. package/v3/dist/sync/embeddings/sync-embedding-generator.js +264 -0
  106. package/v3/dist/sync/embeddings/sync-embedding-generator.js.map +1 -0
  107. package/v3/dist/sync/index.d.ts +1 -0
  108. package/v3/dist/sync/index.d.ts.map +1 -1
  109. package/v3/dist/sync/index.js +2 -0
  110. package/v3/dist/sync/index.js.map +1 -1
  111. package/v3/dist/workers/workers/cloud-sync.d.ts +19 -0
  112. package/v3/dist/workers/workers/cloud-sync.d.ts.map +1 -0
  113. package/v3/dist/workers/workers/cloud-sync.js +177 -0
  114. package/v3/dist/workers/workers/cloud-sync.js.map +1 -0
  115. package/v3/dist/workers/workers/index.d.ts +12 -11
  116. package/v3/dist/workers/workers/index.d.ts.map +1 -1
  117. package/v3/dist/workers/workers/index.js +12 -11
  118. package/v3/dist/workers/workers/index.js.map +1 -1
  119. package/v3/package.json +8 -4
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Infrastructure-Aware Agent Provider
3
+ * ADR-057: Infrastructure Self-Healing Extension
4
+ *
5
+ * Wraps an existing AgentProvider and enriches it with synthetic "agents"
6
+ * representing infrastructure services. When a service is down (detected by
7
+ * TestOutputObserver), the synthetic agent's health metrics reflect the failure,
8
+ * causing the existing Strange Loop to detect it as a vulnerability and trigger
9
+ * healing actions.
10
+ */
11
+ // ============================================================================
12
+ // Infrastructure-Aware Agent Provider
13
+ // ============================================================================
14
+ /**
15
+ * Wraps an AgentProvider and adds synthetic infrastructure agents.
16
+ * Each service in the recovery playbook becomes a synthetic agent.
17
+ * Health metrics are derived from the TestOutputObserver's last observation.
18
+ */
19
+ export class InfraAwareAgentProvider {
20
+ delegate;
21
+ observer;
22
+ playbook;
23
+ prefix;
24
+ constructor(delegate, observer, playbook, infraAgentPrefix = 'infra-') {
25
+ this.delegate = delegate;
26
+ this.observer = observer;
27
+ this.playbook = playbook;
28
+ this.prefix = infraAgentPrefix;
29
+ }
30
+ /**
31
+ * Get all agents: real swarm agents + synthetic infra agents.
32
+ */
33
+ async getAgents() {
34
+ const realAgents = await this.delegate.getAgents();
35
+ const infraAgents = this.createInfraAgents();
36
+ return [...realAgents, ...infraAgents];
37
+ }
38
+ /**
39
+ * Get communication edges: real edges only (infra agents don't communicate).
40
+ */
41
+ async getEdges() {
42
+ return this.delegate.getEdges();
43
+ }
44
+ /**
45
+ * Get agent health. For infra agents, derive from observer state.
46
+ */
47
+ async getAgentHealth(agentId) {
48
+ if (agentId.startsWith(this.prefix)) {
49
+ return this.getInfraAgentHealth(agentId);
50
+ }
51
+ return this.delegate.getAgentHealth(agentId);
52
+ }
53
+ /**
54
+ * Get observer ID (delegates to wrapped provider).
55
+ */
56
+ getObserverId() {
57
+ return this.delegate.getObserverId();
58
+ }
59
+ // ============================================================================
60
+ // Private Methods
61
+ // ============================================================================
62
+ /**
63
+ * Create synthetic AgentNode for each service in the playbook.
64
+ */
65
+ createInfraAgents() {
66
+ const services = this.playbook.listServices();
67
+ const failingServices = this.observer.getFailingServices();
68
+ return services.map((serviceName) => ({
69
+ id: `${this.prefix}${serviceName}`,
70
+ type: 'infrastructure',
71
+ role: 'specialist',
72
+ status: failingServices.has(serviceName) ? 'degraded' : 'active',
73
+ joinedAt: Date.now(),
74
+ metadata: { serviceName, isInfraAgent: true },
75
+ }));
76
+ }
77
+ /**
78
+ * Get health metrics for a synthetic infra agent.
79
+ * Failing services get responsiveness=0, healthy services get 1.0.
80
+ */
81
+ getInfraAgentHealth(agentId) {
82
+ const serviceName = agentId.replace(new RegExp(`^${this.prefix}`), '');
83
+ const failingServices = this.observer.getFailingServices();
84
+ const isFailing = failingServices.has(serviceName);
85
+ return {
86
+ responsiveness: isFailing ? 0.0 : 1.0,
87
+ taskCompletionRate: isFailing ? 0.0 : 1.0,
88
+ memoryUtilization: 0.1,
89
+ cpuUtilization: 0.1,
90
+ activeConnections: 0,
91
+ isBottleneck: false,
92
+ degree: 0,
93
+ queuedTasks: 0,
94
+ lastHeartbeat: isFailing ? 0 : Date.now(),
95
+ errorRate: isFailing ? 1.0 : 0.0,
96
+ };
97
+ }
98
+ }
99
+ // ============================================================================
100
+ // Factory
101
+ // ============================================================================
102
+ /**
103
+ * Factory function for creating an InfraAwareAgentProvider.
104
+ */
105
+ export function createInfraAwareAgentProvider(delegate, observer, playbook, infraAgentPrefix) {
106
+ return new InfraAwareAgentProvider(delegate, observer, playbook, infraAgentPrefix);
107
+ }
108
+ //# sourceMappingURL=infra-aware-agent-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infra-aware-agent-provider.js","sourceRoot":"","sources":["../../../src/strange-loop/infra-healing/infra-aware-agent-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,+EAA+E;AAC/E,sCAAsC;AACtC,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,OAAO,uBAAuB;IACjB,QAAQ,CAAgB;IACxB,QAAQ,CAAqB;IAC7B,QAAQ,CAAmB;IAC3B,MAAM,CAAS;IAEhC,YACE,QAAuB,EACvB,QAA4B,EAC5B,QAA0B,EAC1B,mBAA2B,QAAQ;QAEnC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,gBAAgB,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC7C,OAAO,CAAC,GAAG,UAAU,EAAE,GAAG,WAAW,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,OAAe;QAClC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;IACvC,CAAC;IAED,+EAA+E;IAC/E,kBAAkB;IAClB,+EAA+E;IAE/E;;OAEG;IACK,iBAAiB;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;QAC9C,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QAE3D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YACpC,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,WAAW,EAAE;YAClC,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,YAAqB;YAC3B,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAmB,CAAC,CAAC,CAAC,QAAiB;YAClF,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;YACpB,QAAQ,EAAE,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE;SAC9C,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;OAGG;IACK,mBAAmB,CAAC,OAAe;QACzC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACvE,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QAC3D,MAAM,SAAS,GAAG,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAEnD,OAAO;YACL,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;YACrC,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;YACzC,iBAAiB,EAAE,GAAG;YACtB,cAAc,EAAE,GAAG;YACnB,iBAAiB,EAAE,CAAC;YACpB,YAAY,EAAE,KAAK;YACnB,MAAM,EAAE,CAAC;YACT,WAAW,EAAE,CAAC;YACd,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;YACzC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;SACjC,CAAC;IACJ,CAAC;CACF;AAED,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAC3C,QAAuB,EACvB,QAA4B,EAC5B,QAA0B,EAC1B,gBAAyB;IAEzB,OAAO,IAAI,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;AACrF,CAAC"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Infrastructure Healing Orchestrator
3
+ * ADR-057: Infrastructure Self-Healing Extension
4
+ *
5
+ * Top-level coordinator that wires TestOutputObserver, InfraActionExecutor,
6
+ * RecoveryPlaybook, and CoordinationLock together. Provides a simple API:
7
+ *
8
+ * const orchestrator = createInfraHealingOrchestrator({ ... });
9
+ * orchestrator.feedTestOutput(testStderr);
10
+ * const result = await orchestrator.runRecoveryCycle();
11
+ *
12
+ * Designed to work alongside (not replace) the existing StrangeLoopOrchestrator.
13
+ */
14
+ import type { InfraHealingConfig, InfraHealingStats, CommandRunner, ServiceRecoveryResult } from './types.js';
15
+ import { TestOutputObserver } from './test-output-observer.js';
16
+ import { RecoveryPlaybook } from './recovery-playbook.js';
17
+ import { CoordinationLock } from './coordination-lock.js';
18
+ import { InfraActionExecutor } from './infra-action-executor.js';
19
+ import type { InfraErrorSignature } from './types.js';
20
+ /**
21
+ * Options for creating an InfraHealingOrchestrator.
22
+ * All dependencies injected via this options object.
23
+ */
24
+ export interface InfraHealingOrchestratorOptions {
25
+ /** Command runner for shell execution (inject NoOpCommandRunner for tests) */
26
+ commandRunner: CommandRunner;
27
+ /** YAML playbook content (string) or path to YAML file */
28
+ playbook: string;
29
+ /** Whether playbook is a file path (true) or inline YAML (false) */
30
+ playbookIsFile?: boolean;
31
+ /** Custom error signatures (extends defaults) */
32
+ customSignatures?: readonly InfraErrorSignature[];
33
+ /** Variable overrides for playbook interpolation */
34
+ variables?: Record<string, string>;
35
+ /** Configuration overrides */
36
+ config?: Partial<InfraHealingConfig>;
37
+ }
38
+ /**
39
+ * Orchestrates infrastructure self-healing during test execution.
40
+ *
41
+ * Usage:
42
+ * 1. Create with createInfraHealingOrchestrator()
43
+ * 2. Call feedTestOutput() with test runner stderr/stdout
44
+ * 3. Call runRecoveryCycle() to detect and recover failing services
45
+ * 4. Call getStats() to check recovery metrics
46
+ */
47
+ export declare class InfraHealingOrchestrator {
48
+ private readonly observer;
49
+ private readonly playbook;
50
+ private readonly lock;
51
+ private readonly executor;
52
+ private readonly config;
53
+ private readonly stats;
54
+ private initialized;
55
+ constructor(observer: TestOutputObserver, playbook: RecoveryPlaybook, lock: CoordinationLock, executor: InfraActionExecutor, config: InfraHealingConfig);
56
+ /**
57
+ * Feed test output (stdout + stderr) for analysis.
58
+ * Can be called multiple times — each call observes the new output.
59
+ */
60
+ feedTestOutput(output: string): void;
61
+ /**
62
+ * Run a recovery cycle for all currently-failing services.
63
+ * Returns recovery results for each service attempted.
64
+ */
65
+ runRecoveryCycle(): Promise<readonly ServiceRecoveryResult[]>;
66
+ /**
67
+ * Get the test output observer (for wiring into InfraAwareAgentProvider).
68
+ */
69
+ getObserver(): TestOutputObserver;
70
+ /**
71
+ * Get the recovery playbook (for wiring into InfraAwareAgentProvider).
72
+ */
73
+ getPlaybook(): RecoveryPlaybook;
74
+ /**
75
+ * Get the coordination lock.
76
+ */
77
+ getLock(): CoordinationLock;
78
+ /**
79
+ * Get the infrastructure action executor.
80
+ */
81
+ getExecutor(): InfraActionExecutor;
82
+ /**
83
+ * Get aggregated statistics.
84
+ */
85
+ getStats(): Readonly<InfraHealingStats>;
86
+ /**
87
+ * Check if the orchestrator has been initialized with a loaded playbook.
88
+ */
89
+ isReady(): boolean;
90
+ private mergeExecutorStats;
91
+ }
92
+ /**
93
+ * Factory function for creating a fully-wired InfraHealingOrchestrator.
94
+ * Handles playbook loading, component creation, and dependency wiring.
95
+ */
96
+ export declare function createInfraHealingOrchestrator(options: InfraHealingOrchestratorOptions): Promise<InfraHealingOrchestrator>;
97
+ /**
98
+ * Synchronous factory for creating an InfraHealingOrchestrator with inline YAML.
99
+ * Useful for tests where you don't want async setup.
100
+ */
101
+ export declare function createInfraHealingOrchestratorSync(options: Omit<InfraHealingOrchestratorOptions, 'playbookIsFile'>): InfraHealingOrchestrator;
102
+ //# sourceMappingURL=infra-healing-orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infra-healing-orchestrator.d.ts","sourceRoot":"","sources":["../../../src/strange-loop/infra-healing/infra-healing-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAA4B,MAAM,2BAA2B,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAA0B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAA0B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,mBAAmB,EAA6B,MAAM,4BAA4B,CAAC;AAC5F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMtD;;;GAGG;AACH,MAAM,WAAW,+BAA+B;IAC9C,8EAA8E;IAC9E,aAAa,EAAE,aAAa,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,8BAA8B;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACtC;AAMD;;;;;;;;GAQG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,WAAW,CAAS;gBAG1B,QAAQ,EAAE,kBAAkB,EAC5B,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,mBAAmB,EAC7B,MAAM,EAAE,kBAAkB;IAU5B;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgBpC;;;OAGG;IACG,gBAAgB,IAAI,OAAO,CAAC,SAAS,qBAAqB,EAAE,CAAC;IAqCnE;;OAEG;IACH,WAAW,IAAI,kBAAkB;IAIjC;;OAEG;IACH,WAAW,IAAI,gBAAgB;IAI/B;;OAEG;IACH,OAAO,IAAI,gBAAgB;IAI3B;;OAEG;IACH,WAAW,IAAI,mBAAmB;IAIlC;;OAEG;IACH,QAAQ,IAAI,QAAQ,CAAC,iBAAiB,CAAC;IAKvC;;OAEG;IACH,OAAO,IAAI,OAAO;IAQlB,OAAO,CAAC,kBAAkB;CAe3B;AAMD;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,wBAAwB,CAAC,CAuBnC;AAED;;;GAGG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,IAAI,CAAC,+BAA+B,EAAE,gBAAgB,CAAC,GAC/D,wBAAwB,CAgB1B"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Infrastructure Healing Orchestrator
3
+ * ADR-057: Infrastructure Self-Healing Extension
4
+ *
5
+ * Top-level coordinator that wires TestOutputObserver, InfraActionExecutor,
6
+ * RecoveryPlaybook, and CoordinationLock together. Provides a simple API:
7
+ *
8
+ * const orchestrator = createInfraHealingOrchestrator({ ... });
9
+ * orchestrator.feedTestOutput(testStderr);
10
+ * const result = await orchestrator.runRecoveryCycle();
11
+ *
12
+ * Designed to work alongside (not replace) the existing StrangeLoopOrchestrator.
13
+ */
14
+ import { v4 as uuidv4 } from 'uuid';
15
+ import { DEFAULT_INFRA_HEALING_CONFIG, createEmptyStats } from './types.js';
16
+ import { createTestOutputObserver } from './test-output-observer.js';
17
+ import { createRecoveryPlaybook } from './recovery-playbook.js';
18
+ import { createCoordinationLock } from './coordination-lock.js';
19
+ import { createInfraActionExecutor } from './infra-action-executor.js';
20
+ // ============================================================================
21
+ // Infrastructure Healing Orchestrator
22
+ // ============================================================================
23
+ /**
24
+ * Orchestrates infrastructure self-healing during test execution.
25
+ *
26
+ * Usage:
27
+ * 1. Create with createInfraHealingOrchestrator()
28
+ * 2. Call feedTestOutput() with test runner stderr/stdout
29
+ * 3. Call runRecoveryCycle() to detect and recover failing services
30
+ * 4. Call getStats() to check recovery metrics
31
+ */
32
+ export class InfraHealingOrchestrator {
33
+ observer;
34
+ playbook;
35
+ lock;
36
+ executor;
37
+ config;
38
+ stats;
39
+ initialized = false;
40
+ constructor(observer, playbook, lock, executor, config) {
41
+ this.observer = observer;
42
+ this.playbook = playbook;
43
+ this.lock = lock;
44
+ this.executor = executor;
45
+ this.config = config;
46
+ this.stats = createEmptyStats();
47
+ }
48
+ /**
49
+ * Feed test output (stdout + stderr) for analysis.
50
+ * Can be called multiple times — each call observes the new output.
51
+ */
52
+ feedTestOutput(output) {
53
+ const observation = this.observer.observe(output);
54
+ this.stats.totalObservations++;
55
+ this.stats.infraFailuresDetected += observation.infraFailures.length;
56
+ // Update per-service failure counts
57
+ for (const failure of observation.infraFailures) {
58
+ if (failure.serviceName) {
59
+ if (!this.stats.byService[failure.serviceName]) {
60
+ this.stats.byService[failure.serviceName] = { failures: 0, recoveries: 0, successes: 0 };
61
+ }
62
+ this.stats.byService[failure.serviceName].failures++;
63
+ }
64
+ }
65
+ }
66
+ /**
67
+ * Run a recovery cycle for all currently-failing services.
68
+ * Returns recovery results for each service attempted.
69
+ */
70
+ async runRecoveryCycle() {
71
+ const failingServices = this.observer.getFailingServices();
72
+ if (failingServices.size === 0)
73
+ return [];
74
+ const results = [];
75
+ let activeRecoveries = 0;
76
+ for (const serviceName of failingServices) {
77
+ if (activeRecoveries >= this.config.maxConcurrentRecoveries)
78
+ break;
79
+ // Skip if no playbook entry
80
+ if (!this.playbook.getRecoveryPlan(serviceName))
81
+ continue;
82
+ // Skip if already locked (recovery in progress)
83
+ if (this.lock.isLocked(serviceName)) {
84
+ this.stats.lockContentionEvents++;
85
+ continue;
86
+ }
87
+ activeRecoveries++;
88
+ const actionId = uuidv4();
89
+ const result = await this.executor.recoverService(serviceName, actionId);
90
+ results.push(result);
91
+ // Merge executor stats
92
+ this.mergeExecutorStats();
93
+ }
94
+ // Clear observation if all services recovered
95
+ const stillFailing = this.observer.getFailingServices();
96
+ if (stillFailing.size === 0) {
97
+ this.observer.clearObservation();
98
+ }
99
+ return results;
100
+ }
101
+ /**
102
+ * Get the test output observer (for wiring into InfraAwareAgentProvider).
103
+ */
104
+ getObserver() {
105
+ return this.observer;
106
+ }
107
+ /**
108
+ * Get the recovery playbook (for wiring into InfraAwareAgentProvider).
109
+ */
110
+ getPlaybook() {
111
+ return this.playbook;
112
+ }
113
+ /**
114
+ * Get the coordination lock.
115
+ */
116
+ getLock() {
117
+ return this.lock;
118
+ }
119
+ /**
120
+ * Get the infrastructure action executor.
121
+ */
122
+ getExecutor() {
123
+ return this.executor;
124
+ }
125
+ /**
126
+ * Get aggregated statistics.
127
+ */
128
+ getStats() {
129
+ this.mergeExecutorStats();
130
+ return this.stats;
131
+ }
132
+ /**
133
+ * Check if the orchestrator has been initialized with a loaded playbook.
134
+ */
135
+ isReady() {
136
+ return this.playbook.isLoaded();
137
+ }
138
+ // ============================================================================
139
+ // Private Methods
140
+ // ============================================================================
141
+ mergeExecutorStats() {
142
+ const execStats = this.executor.getStats();
143
+ this.stats.recoveriesAttempted = execStats.recoveriesAttempted;
144
+ this.stats.recoveriesSucceeded = execStats.recoveriesSucceeded;
145
+ this.stats.recoveriesFailed = execStats.recoveriesFailed;
146
+ // Merge per-service recovery stats from executor
147
+ for (const [service, execServiceStats] of Object.entries(execStats.byService)) {
148
+ if (!this.stats.byService[service]) {
149
+ this.stats.byService[service] = { failures: 0, recoveries: 0, successes: 0 };
150
+ }
151
+ this.stats.byService[service].recoveries = execServiceStats.recoveries;
152
+ this.stats.byService[service].successes = execServiceStats.successes;
153
+ }
154
+ }
155
+ }
156
+ // ============================================================================
157
+ // Factory
158
+ // ============================================================================
159
+ /**
160
+ * Factory function for creating a fully-wired InfraHealingOrchestrator.
161
+ * Handles playbook loading, component creation, and dependency wiring.
162
+ */
163
+ export async function createInfraHealingOrchestrator(options) {
164
+ const config = {
165
+ ...DEFAULT_INFRA_HEALING_CONFIG,
166
+ ...options.config,
167
+ };
168
+ // Create components
169
+ const observer = createTestOutputObserver(options.customSignatures);
170
+ const playbook = createRecoveryPlaybook(options.variables);
171
+ const lock = createCoordinationLock(config.lockTtlMs);
172
+ // Load playbook
173
+ if (options.playbookIsFile !== false && !options.playbook.includes('\n')) {
174
+ // Looks like a file path
175
+ await playbook.loadFromFile(options.playbook);
176
+ }
177
+ else {
178
+ // Inline YAML content
179
+ playbook.loadFromString(options.playbook);
180
+ }
181
+ const executor = createInfraActionExecutor(options.commandRunner, playbook, lock);
182
+ return new InfraHealingOrchestrator(observer, playbook, lock, executor, config);
183
+ }
184
+ /**
185
+ * Synchronous factory for creating an InfraHealingOrchestrator with inline YAML.
186
+ * Useful for tests where you don't want async setup.
187
+ */
188
+ export function createInfraHealingOrchestratorSync(options) {
189
+ const config = {
190
+ ...DEFAULT_INFRA_HEALING_CONFIG,
191
+ ...options.config,
192
+ };
193
+ const observer = createTestOutputObserver(options.customSignatures);
194
+ const playbook = createRecoveryPlaybook(options.variables);
195
+ const lock = createCoordinationLock(config.lockTtlMs);
196
+ // Load inline YAML
197
+ playbook.loadFromString(options.playbook);
198
+ const executor = createInfraActionExecutor(options.commandRunner, playbook, lock);
199
+ return new InfraHealingOrchestrator(observer, playbook, lock, executor, config);
200
+ }
201
+ //# sourceMappingURL=infra-healing-orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infra-healing-orchestrator.js","sourceRoot":"","sources":["../../../src/strange-loop/infra-healing/infra-healing-orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Recovery Playbook
3
+ * ADR-057: Infrastructure Self-Healing Extension
4
+ *
5
+ * YAML-driven configuration for infrastructure recovery.
6
+ * Maps service names to health-check / recover / verify shell commands.
7
+ * Framework-agnostic — changing the YAML switches from Jest to Pytest
8
+ * to JUnit with zero code changes.
9
+ *
10
+ * Supports ${VAR} interpolation from environment variables or an
11
+ * explicit variables map.
12
+ *
13
+ * ## Security: Trust Boundary
14
+ *
15
+ * Playbook commands are passed to ShellCommandRunner which executes them
16
+ * via `execFile('/bin/sh', ['-c', command])`. The ${VAR} interpolation
17
+ * merges process.env with any explicit variables map — if an environment
18
+ * variable contains shell metacharacters (e.g. `PGHOST="localhost; rm -rf /"`),
19
+ * those will be interpreted by the shell.
20
+ *
21
+ * **Mitigation**: Playbook YAML must come from trusted configuration (checked
22
+ * into source control, operator-managed). Do NOT load playbooks from untrusted
23
+ * user input. Do NOT allow untrusted processes to set environment variables
24
+ * that playbook commands reference.
25
+ */
26
+ import type { RecoveryPlaybookConfig, ServiceRecoveryPlan } from './types.js';
27
+ /**
28
+ * Loads and manages a YAML recovery playbook.
29
+ * Provides service recovery plans with variable interpolation.
30
+ */
31
+ export declare class RecoveryPlaybook {
32
+ private config;
33
+ private readonly variables;
34
+ constructor(variables?: Record<string, string>);
35
+ /**
36
+ * Load playbook from a YAML file path.
37
+ */
38
+ loadFromFile(filePath: string): Promise<void>;
39
+ /**
40
+ * Load playbook from a YAML string (for testing or inline config).
41
+ */
42
+ loadFromString(yamlContent: string): void;
43
+ /**
44
+ * Get the recovery plan for a specific service.
45
+ * Returns undefined if the service is not defined in the playbook.
46
+ */
47
+ getRecoveryPlan(serviceName: string): ServiceRecoveryPlan | undefined;
48
+ /**
49
+ * List all service names defined in the playbook.
50
+ */
51
+ listServices(): readonly string[];
52
+ /**
53
+ * Check if the playbook has been loaded.
54
+ */
55
+ isLoaded(): boolean;
56
+ /**
57
+ * Get the full playbook configuration.
58
+ */
59
+ getConfig(): RecoveryPlaybookConfig | null;
60
+ private parsePlaybook;
61
+ private parseService;
62
+ private parseCommand;
63
+ /**
64
+ * Interpolate ${VAR} placeholders in a command string.
65
+ * Uses the variables map (env vars + explicit overrides).
66
+ * Leaves unresolved variables as-is (does not fail).
67
+ */
68
+ private interpolate;
69
+ }
70
+ /**
71
+ * Factory function for creating a RecoveryPlaybook.
72
+ */
73
+ export declare function createRecoveryPlaybook(variables?: Record<string, string>): RecoveryPlaybook;
74
+ //# sourceMappingURL=recovery-playbook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-playbook.d.ts","sourceRoot":"","sources":["../../../src/strange-loop/infra-healing/recovery-playbook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AAMpB;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;gBAEvC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAI9C;;OAEG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKzC;;;OAGG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAKrE;;OAEG;IACH,YAAY,IAAI,SAAS,MAAM,EAAE;IAKjC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,SAAS,IAAI,sBAAsB,GAAG,IAAI;IAQ1C,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,YAAY;IAoBpB,OAAO,CAAC,YAAY;IAepB;;;;OAIG;IACH,OAAO,CAAC,WAAW;CAKpB;AAmCD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,gBAAgB,CAElB"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Recovery Playbook
3
+ * ADR-057: Infrastructure Self-Healing Extension
4
+ *
5
+ * YAML-driven configuration for infrastructure recovery.
6
+ * Maps service names to health-check / recover / verify shell commands.
7
+ * Framework-agnostic — changing the YAML switches from Jest to Pytest
8
+ * to JUnit with zero code changes.
9
+ *
10
+ * Supports ${VAR} interpolation from environment variables or an
11
+ * explicit variables map.
12
+ *
13
+ * ## Security: Trust Boundary
14
+ *
15
+ * Playbook commands are passed to ShellCommandRunner which executes them
16
+ * via `execFile('/bin/sh', ['-c', command])`. The ${VAR} interpolation
17
+ * merges process.env with any explicit variables map — if an environment
18
+ * variable contains shell metacharacters (e.g. `PGHOST="localhost; rm -rf /"`),
19
+ * those will be interpreted by the shell.
20
+ *
21
+ * **Mitigation**: Playbook YAML must come from trusted configuration (checked
22
+ * into source control, operator-managed). Do NOT load playbooks from untrusted
23
+ * user input. Do NOT allow untrusted processes to set environment variables
24
+ * that playbook commands reference.
25
+ */
26
+ import * as fs from 'node:fs/promises';
27
+ import { parse as parseYaml } from 'yaml';
28
+ // ============================================================================
29
+ // Recovery Playbook
30
+ // ============================================================================
31
+ /**
32
+ * Loads and manages a YAML recovery playbook.
33
+ * Provides service recovery plans with variable interpolation.
34
+ */
35
+ export class RecoveryPlaybook {
36
+ config = null;
37
+ variables;
38
+ constructor(variables) {
39
+ this.variables = { ...process.env, ...(variables ?? {}) };
40
+ }
41
+ /**
42
+ * Load playbook from a YAML file path.
43
+ */
44
+ async loadFromFile(filePath) {
45
+ const content = await fs.readFile(filePath, 'utf-8');
46
+ this.loadFromString(content);
47
+ }
48
+ /**
49
+ * Load playbook from a YAML string (for testing or inline config).
50
+ */
51
+ loadFromString(yamlContent) {
52
+ const raw = parseYaml(yamlContent);
53
+ this.config = this.parsePlaybook(raw);
54
+ }
55
+ /**
56
+ * Get the recovery plan for a specific service.
57
+ * Returns undefined if the service is not defined in the playbook.
58
+ */
59
+ getRecoveryPlan(serviceName) {
60
+ if (!this.config)
61
+ return undefined;
62
+ return this.config.services[serviceName];
63
+ }
64
+ /**
65
+ * List all service names defined in the playbook.
66
+ */
67
+ listServices() {
68
+ if (!this.config)
69
+ return [];
70
+ return Object.keys(this.config.services);
71
+ }
72
+ /**
73
+ * Check if the playbook has been loaded.
74
+ */
75
+ isLoaded() {
76
+ return this.config !== null;
77
+ }
78
+ /**
79
+ * Get the full playbook configuration.
80
+ */
81
+ getConfig() {
82
+ return this.config;
83
+ }
84
+ // ============================================================================
85
+ // Parsing
86
+ // ============================================================================
87
+ parsePlaybook(raw) {
88
+ const defaults = raw.defaults ?? {};
89
+ const defaultTimeoutMs = defaults.timeoutMs ?? 10_000;
90
+ const defaultMaxRetries = defaults.maxRetries ?? 3;
91
+ const defaultBackoffMs = defaults.backoffMs ?? [2000, 5000, 10_000];
92
+ const services = {};
93
+ if (raw.services) {
94
+ for (const [name, rawService] of Object.entries(raw.services)) {
95
+ services[name] = this.parseService(name, rawService, defaultTimeoutMs, defaultMaxRetries, defaultBackoffMs);
96
+ }
97
+ }
98
+ return {
99
+ version: raw.version ?? '1.0.0',
100
+ defaultTimeoutMs,
101
+ defaultMaxRetries,
102
+ defaultBackoffMs,
103
+ services,
104
+ };
105
+ }
106
+ parseService(name, raw, defaultTimeoutMs, defaultMaxRetries, defaultBackoffMs) {
107
+ return {
108
+ serviceName: name,
109
+ description: raw.description ?? name,
110
+ healthCheck: this.parseCommand(raw.healthCheck, defaultTimeoutMs),
111
+ recover: Array.isArray(raw.recover)
112
+ ? raw.recover.map((cmd) => this.parseCommand(cmd, defaultTimeoutMs))
113
+ : [this.parseCommand(raw.recover, defaultTimeoutMs)],
114
+ verify: this.parseCommand(raw.verify, defaultTimeoutMs),
115
+ maxRetries: raw.maxRetries ?? defaultMaxRetries,
116
+ backoffMs: raw.backoffMs ?? defaultBackoffMs,
117
+ };
118
+ }
119
+ parseCommand(raw, defaultTimeoutMs) {
120
+ if (typeof raw === 'string') {
121
+ return {
122
+ command: this.interpolate(raw),
123
+ timeoutMs: defaultTimeoutMs,
124
+ required: true,
125
+ };
126
+ }
127
+ return {
128
+ command: this.interpolate(raw.command),
129
+ timeoutMs: raw.timeoutMs ?? defaultTimeoutMs,
130
+ required: raw.required ?? true,
131
+ };
132
+ }
133
+ /**
134
+ * Interpolate ${VAR} placeholders in a command string.
135
+ * Uses the variables map (env vars + explicit overrides).
136
+ * Leaves unresolved variables as-is (does not fail).
137
+ */
138
+ interpolate(template) {
139
+ return template.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
140
+ return this.variables[varName] ?? _match;
141
+ });
142
+ }
143
+ }
144
+ // ============================================================================
145
+ // Factory
146
+ // ============================================================================
147
+ /**
148
+ * Factory function for creating a RecoveryPlaybook.
149
+ */
150
+ export function createRecoveryPlaybook(variables) {
151
+ return new RecoveryPlaybook(variables);
152
+ }
153
+ //# sourceMappingURL=recovery-playbook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-playbook.js","sourceRoot":"","sources":["../../../src/strange-loop/infra-healing/recovery-playbook.ts"],"names":[],"mappings":"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"}